Không thấy bất cứ điều gì tương tự và tất cả các chức năng tùy chỉnh ở đây dường như chỉ tập trung vào kết xuất, vì vậy ... giải pháp tuân thủ POSIX rất đơn giản của tôi dưới đây với các giải thích từng bước vì câu hỏi này không tầm thường.
TL; DR
Kết xuất thanh tiến trình là rất dễ dàng. Ước tính bao nhiêu của nó sẽ kết xuất là một vấn đề khác nhau. Đây là cách kết xuất (hoạt hình) thanh tiến trình - bạn có thể sao chép và dán ví dụ này vào một tệp và chạy nó:
#!/bin/sh
BAR='####################' # this is full bar, e.g. 20 chars
for i in {1..20}; do
echo -ne "\r${BAR:0:$i}" # print $i chars of $BAR from 0 position
sleep .1 # wait 100ms between "frames"
done
{1..20}
- các giá trị từ 1 đến 20
echo -n
- in không có dòng mới ở cuối
echo -e
- giải thích các ký tự đặc biệt trong khi in
"\r"
- Vận chuyển trở lại, một char đặc biệt để trở về đầu dòng
Bạn có thể làm cho nó hiển thị bất kỳ nội dung nào ở bất kỳ tốc độ nào, vì vậy phương pháp này rất phổ biến, ví dụ như thường được sử dụng để trực quan hóa "hack" trong các bộ phim ngớ ngẩn, không đùa.
Câu trả lời đầy đủ
Phần cốt lõi của vấn đề là làm thế nào để xác định $i
giá trị, tức là bao nhiêu thanh tiến trình sẽ hiển thị. Trong ví dụ trên tôi chỉ để nó tăng theo for
vòng lặp để minh họa nguyên tắc nhưng một ứng dụng thực tế sẽ sử dụng một vòng lặp vô hạn và tính toán $i
biến trên mỗi lần lặp. Để thực hiện tính toán, nó cần các thành phần sau:
- có bao nhiêu công việc phải làm
- bao nhiêu công việc đã được thực hiện cho đến nay
Trong trường hợp cp
nó cần kích thước của tệp nguồn và kích thước của tệp đích:
#!/bin/sh
$src=/path/to/source/file
$tgt=/path/to/target/file
cp "$src" "$tgt" & # the & forks the `cp` process so the rest
# of the code runs without waiting (async)
BAR='####################'
src_size=$(stat -c%s "$src") # how much there is to do
while true; do
tgt_size=$(stat -c%s "$tgt") # how much has been done so far
i=$(( $tgt_size * 20 / $src_size ))
echo -ne "\r${BAR:0:$i}"
if [ $tgt_size == $src_size ]; then
echo "" # add a new line at the end
break; # break the loop
fi
sleep .1
done
stat
- kiểm tra số liệu tập tin
-c
- trả về giá trị định dạng
%s
- tổng kích thước
Trong trường hợp các thao tác như giải nén tệp, việc tính toán kích thước nguồn hơi khó khăn hơn một chút nhưng vẫn dễ như lấy kích thước của tệp không nén:
#!/bin/sh
src_size=$(gzip -l "$src" | tail -n1 | tr -s ' ' | cut -d' ' -f3)
gzip -l
- hiển thị thông tin về kho lưu trữ zip
tail -n1
- làm việc với 1 dòng từ dưới lên
tr -s ' '
- dịch nhiều khoảng trắng thành một (ép chúng)
cut -d' ' -f3
- cắt cột phân cách không gian thứ 3
Đây là phần cốt lõi của vấn đề. Giải pháp này ngày càng ít chung chung. Do đó, tất cả các tính toán về tiến trình thực tế đều bị ràng buộc chặt chẽ với miền mà bạn đang cố gắng hình dung, đó có phải là một thao tác tệp duy nhất, đếm ngược thời gian, số lượng tệp tăng lên trong một thư mục, hoạt động trên nhiều tệp, v.v. không thể được sử dụng lại. Phần duy nhất có thể tái sử dụng là kết xuất thanh tiến trình. Để sử dụng lại, bạn cần trừu tượng hóa nó và lưu trong một tệp (ví dụ /usr/lib/progress_bar.sh
), sau đó xác định các hàm tính giá trị đầu vào cụ thể cho miền của bạn. Đây là cách một mã tổng quát có thể trông như thế nào (Tôi cũng đã tạo ra sự $BAR
năng động bởi vì mọi người đang yêu cầu nó, phần còn lại sẽ rõ ràng ngay bây giờ):
#!/bin/sh
BAR_length=50
BAR_character='#'
BAR=$(printf "%${BAR_length}s" | tr ' ' $BAR_character)
work_todo=$(get_work_todo) # how much there is to do
while true; do
work_done=$(get_work_done) # how much has been done so far
i=$(( $work_done * $BAR_length / $work_todo ))
echo -ne "\r${BAR:0:$i}"
if [ $work_done == $work_todo ]; then
echo ""
break;
fi
sleep .1
done
printf
- một nội dung để in các công cụ theo định dạng nhất định
printf '%50s'
- không in gì, đệm nó với 50 dấu cách
tr ' ' '#'
- dịch mọi chỗ trống sang dấu băm
Và đây là cách bạn sử dụng nó:
#!/bin/sh
src=/path/to/source/file
tgt=/path/to/target/file
function get_work_todo() {
echo $(stat -c%s "$src")
}
function get_work_done() {
[ -e "$tgt" ] && # if target file exists
echo $(stat -c%s "$tgt") || # echo its size, else
echo 0 # echo zero
}
cp "$src" "$tgt" & # copy in the background
source /usr/lib/progress_bar.sh # execute the progress bar
Rõ ràng nó có thể được bọc trong một chức năng, được viết lại để làm việc với các luồng được truyền, viết lại sang ngôn ngữ khác, bất kể là chất độc của bạn.