Я не бачив нічого подібного, і всі спеціальні функції тут, схоже, зосереджені на рендерінгу, тому моє дуже просте рішення, сумісне з POSIX нижче, покрокові пояснення, оскільки це питання не є тривіальним.
TL; DR
Надання смужки прогресу дуже просто. Оцінка того, яку саме частину має скласти, є різною справою. Ось як візуалізувати (анімувати) панель прогресу - ви можете скопіювати та вставити цей приклад у файл та запустити його:
#!/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} - значення від 1 до 20
echo -n - друк без нового рядка в кінці
echo -e - інтерпретувати спеціальні символи під час друку
"\r" - повернення вагона, спеціальний знак для повернення на початок рядка
Ви можете змусити його виводити будь-який вміст з будь-якою швидкістю, тому цей метод є дуже універсальним, наприклад, часто використовується для візуалізації "злому" в нерозумних фільмах, не жартуючи.
Повна відповідь
Основна проблема полягає в тому, як визначити $iзначення, тобто скільки відображається смуга прогресу. У наведеному вище прикладі я просто дозволю збільшенню в forциклі, щоб проілюструвати принцип, але в реальному застосуванні буде використано нескінченний цикл і обчислити $iзмінну на кожній ітерації. Для здійснення зазначеного розрахунку йому потрібні такі інгредієнти:
- скільки роботи потрібно виконати
- скільки роботи зроблено досі
У разі cpпотреби потрібний розмір вихідного файлу та розмір цільового файлу:
#!/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 - перевірка статистики файлів
-c - повернути відформатоване значення
%s - загальний розмір
У таких операціях, як розпакування файлу, обчислити розмір джерела дещо складніше, але все одно так само просто, як отримати розмір нестисненого файлу:
#!/bin/sh
src_size=$(gzip -l "$src" | tail -n1 | tr -s ' ' | cut -d' ' -f3)
gzip -l - відображення інформації про zip-архів
tail -n1 - робота з 1 рядком знизу
tr -s ' ' - перекладіть кілька пробілів в один (стисніть їх)
cut -d' ' -f3 - вирізати 3-й стовпчик з обмеженим пробілом
Ось у цьому і полягає проблема. Це рішення все менш і менш загальне. Всі обчислення фактичного прогресу тісно пов'язані з доменом, який ви намагаєтеся візуалізувати, це одна операція з файлом, відлік таймера, зростаюча кількість файлів у каталозі, робота з декількома файлами тощо, отже, це не можуть бути використані повторно. Єдина частина багаторазового використання - це візуалізація смуги прогресу. Для його повторного використання потрібно абстрагувати його та зберегти у файлі (наприклад /usr/lib/progress_bar.sh), а потім визначити функції, які обчислюють вхідні значення, характерні для вашого домену. Ось як може виглядати узагальнений код (я також зробив $BARдинамічний, тому що люди просили його, решта має бути зрозумілою вже зараз):
#!/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 - вбудований для друку матеріалів у заданому форматі
printf '%50s' - нічого не друкуйте, накладайте його на 50 пробілів
tr ' ' '#' - перекладіть кожен пробіл у хеш-знак
І ось як би ви його використовували:
#!/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
Очевидно, що його можна загорнути у функцію, переписати для роботи з трубопроводами, переписати на іншу мову, що б не було вашою отрутою.