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