Я не бачив нічого подібного, і всі спеціальні функції тут, схоже, зосереджені на рендерінгу, тому моє дуже просте рішення, сумісне з 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
Очевидно, що його можна загорнути у функцію, переписати для роботи з трубопроводами, переписати на іншу мову, що б не було вашою отрутою.