Как добавить индикатор выполнения в сценарий оболочки? - PullRequest
361 голосов
/ 26 октября 2008

При выполнении сценариев в bash или любой другой оболочке в * NIX при выполнении команды, которая займет более нескольких секунд, требуется индикатор выполнения.

Например, копирование большого файла, открытие большого файла tar.

Какими способами вы рекомендуете добавлять индикаторы выполнения в сценарии оболочки?

Ответы [ 36 ]

647 голосов
/ 26 октября 2008

Вы можете реализовать это, переписав строку. Используйте \r для возврата к началу строки без записи \n в терминал.

Напишите \n, когда закончите продвигать линию.

Используйте echo -ne для:

  1. не печатать \n и
  2. для распознавания escape-последовательностей, таких как \r.

Вот демонстрация:

echo -ne '#####                     (33%)\r'
sleep 1
echo -ne '#############             (66%)\r'
sleep 1
echo -ne '#######################   (100%)\r'
echo -ne '\n'

В комментарии ниже, puk упоминает, что это «не получается», если вы начинаете с длинной строки, а затем хотите написать короткую строку: в этом случае вам потребуется перезаписать длину длинной строки (например, пространства).

60 голосов
/ 26 июля 2010

Вас также может заинтересовать как сделать прядильщик :

Могу ли я сделать спиннер в Bash?

Конечно!

i=1
sp="/-\|"
echo -n ' '
while true
do
    printf "\b${sp:i++%${#sp}:1}"
done

Каждый раз, когда цикл повторяется, он отображает следующий символ в sp Строка, оборачивающаяся вокруг, когда она достигает конца. (я позиция текущий символ для отображения и $ {# sp} является длиной sp строка).

Строка \ b заменяется символом «возврат». С другой стороны, вы можете поиграть с \ r, чтобы вернуться к началу строки.

Если вы хотите, чтобы это замедлилось, поместите команду сна в цикл (после печати).

POSIX-эквивалент будет:

sp='/-\|'
printf ' '
while true; do
    printf '\b%.1s' "$sp"
    sp=${sp#?}${sp%???}
done

Если у вас уже есть цикл, который выполняет много работы, вы можете вызвать Следующая функция в начале каждой итерации, чтобы обновить вертушка:

sp="/-\|"
sc=0
spin() {
   printf "\b${sp:sc++:1}"
   ((sc==${#sp})) && sc=0
}
endspin() {
   printf "\r%s\n" "$@"
}

until work_done; do
   spin
   some_work ...
done
endspin
48 голосов
/ 26 октября 2008

В некоторых сообщениях показано, как отображать ход выполнения команды. Чтобы рассчитать это, вам нужно увидеть, насколько вы продвинулись. В системах BSD некоторые команды, такие как dd (1), принимают сигнал SIGINFO и сообщают о своем прогрессе. В системах Linux некоторые команды будут реагировать аналогично SIGUSR1. Если эта возможность доступна, вы можете направить свой ввод через dd, чтобы отслеживать количество обработанных байтов.

В качестве альтернативы вы можете использовать lsof, чтобы получить смещение указателя чтения файла и, таким образом, рассчитать прогресс. Я написал команду с именем pmonitor , которая отображает ход обработки указанного процесса или файла. С его помощью вы можете делать такие вещи, как следующие.

$ pmonitor -c gzip
/home/dds/data/mysql-2015-04-01.sql.gz 58.06%

Более ранняя версия сценариев оболочки Linux и FreeBSD появляется в моем блоге .

40 голосов
/ 28 июля 2011

используйте команду linux pv:

http://linux.die.net/man/1/pv

он не знает размер, если он находится в середине потока, но он дает скорость и общее количество, и оттуда вы можете выяснить, сколько времени это займет, и получить обратную связь, чтобы вы знали, что она не зависла.

36 голосов
/ 20 января 2015

Получил простую функцию индикатора выполнения, которую я написал на днях:

#!/bin/bash
# 1. Create ProgressBar function
# 1.1 Input is currentState($1) and totalState($2)
function ProgressBar {
# Process data
    let _progress=(${1}*100/${2}*100)/100
    let _done=(${_progress}*4)/10
    let _left=40-$_done
# Build progressbar string lengths
    _fill=$(printf "%${_done}s")
    _empty=$(printf "%${_left}s")

# 1.2 Build progressbar strings and print the ProgressBar line
# 1.2.1 Output example:                           
# 1.2.1.1 Progress : [########################################] 100%
printf "\rProgress : [${_fill// /\#}${_empty// /-}] ${_progress}%%"

}

# Variables
_start=1

# This accounts as the "totalState" variable for the ProgressBar function
_end=100

# Proof of concept
for number in $(seq ${_start} ${_end})
do
    sleep 0.1
    ProgressBar ${number} ${_end}
done
printf '\nFinished!\n'

Или поймать его,
https://github.com/fearside/ProgressBar/

23 голосов
/ 06 октября 2016

Я искал что-то более сексуальное, чем выбранный ответ, как и мой собственный сценарий.

Предварительный просмотр

progress-bar.sh in action

Источник

Я положил его на GitHub progress-bar.sh

progress-bar() {
  local duration=${1}


    already_done() { for ((done=0; done<$elapsed; done++)); do printf "▇"; done }
    remaining() { for ((remain=$elapsed; remain<$duration; remain++)); do printf " "; done }
    percentage() { printf "| %s%%" $(( (($elapsed)*100)/($duration)*100/100 )); }
    clean_line() { printf "\r"; }

  for (( elapsed=1; elapsed<=$duration; elapsed++ )); do
      already_done; remaining; percentage
      sleep 1
      clean_line
  done
  clean_line
}

Использование

 progress-bar 100
17 голосов
/ 02 августа 2010

GNU tar имеет полезную опцию, которая дает функциональность простого индикатора выполнения.

(...) Другое доступное действие для контрольной точки - «точка» (или «.»). Он указывает tar распечатать одну точку в стандартном потоке листинга, например ::10000

$ tar -c --checkpoint=1000 --checkpoint-action=dot /var
...

Тот же эффект может быть получен:

$ tar -c --checkpoint=.1000 /var
12 голосов
/ 26 июля 2010

Более простой метод, который работает в моей системе с использованием утилиты pipeview (pv).

srcdir=$1
outfile=$2


tar -Ocf - $srcdir | pv -i 1 -w 50 -berps `du -bs $srcdir | awk '{print $1}'` | 7za a -si $outfile
11 голосов
/ 19 июля 2017

Я также хотел бы добавить свой собственный индикатор прогресса

Достигает точности субсимволов, используя Половина блоков Unicode

enter image description here

Код включен

7 голосов
/ 01 декабря 2016

Ничего подобного не видел, так что ... мое очень простое решение:

#!/bin/bash
BAR='####################'   # this is full bar, mine is 20 chars
for i in {1..20}; do
    echo -ne "\r${BAR:0:$i}" # print $i chars of $BAR from 0 position
    sleep .1
done
  • echo -n - печать без новой строки в конце
  • echo -e - интерпретировать специальные символы при печати
  • "\r" - возврат каретки, специальный символ для возврата в начало строки

Я использовал его давным-давно в простом "хакерском видео" для имитации набора кода. ;)

...