При создании сценариев в bash или любой другой оболочке в * NIX при выполнении команды, которая займет более нескольких секунд, требуется индикатор выполнения.
Например, копирование большого файла, открытие большого файла tar.
Какими способами вы рекомендуете добавлять индикаторы выполнения в сценарии оболочки?
Вы можете реализовать это, переписав строку. Используйте \r
, чтобы вернуться к началу строки, не записывая \n
в терминал.
Напишите \n
, когда закончите продвигать линию.
Используйте echo -ne
, чтобы:
\n
и \r
. Вот демо:
echo -ne '##### (33%)\r'
sleep 1
echo -ne '############# (66%)\r'
sleep 1
echo -ne '####################### (100%)\r'
echo -ne '\n'
В комментарии ниже, puk упоминает, что это «не получается», если вы начинаете с длинной строки, а затем хотите написать короткую строку: в этом случае, вам нужно будет перезаписать длину длинной строки (например, пробелами).
Я сделал чисто версию оболочки для встроенной системы, используя преимущества:
/ usr / bin / dd SIGUSR1 для обработки сигналов.
По сути, если вы отправите 'kill SIGUSR1 $ (pid_of_running_dd_process)', он выведет сводную информацию о скорости передачи и количестве передаваемых данных.
Фоновая обработка dd, а затем регулярный запрос его на наличие обновлений и генерация тиковых хешей, как раньше использовали старые ftp-клиенты.
Использование / dev / stdout в качестве места назначения для дружественных к stdout программ, таких как scp
Конечный результат позволяет вам выполнить любую операцию передачи файла и получить обновление прогресса, которое Похоже на старый хеш-вывод FTP, где вы просто получите хеш-метку для каждого X байтов.
Это едва ли код качества производства, но вы поняли идею. Я думаю, что это мило.
Для чего бы это ни стоило, фактическое количество байтов может не отражаться правильно в количестве хэшей - у вас может быть один больше или меньше в зависимости от проблем округления. Не используйте это как часть тестового сценария, это просто конфетка. И, да, я знаю, что это ужасно неэффективно - это сценарий оболочки, и я не извиняюсь за это.
Примеры с wget, scp и tftp в конце. Он должен работать со всем, что имеет данные. Обязательно используйте / dev / stdout для программ, которые не подходят для stdout.
#!/bin/sh
#
# Copyright (C) Nathan Ramella (nar+progress-script@remix.net) 2010
# LGPLv2 license
# If you use this, send me an email to say thanks and let me know what your product
# is so I can tell all my friends I'm a big man on the internet!
progress_filter() {
local START=$(date +"%s")
local SIZE=1
local DURATION=1
local BLKSZ=51200
local TMPFILE=/tmp/tmpfile
local PROGRESS=/tmp/tftp.progress
local BYTES_LAST_CYCLE=0
local BYTES_THIS_CYCLE=0
rm -f ${PROGRESS}
dd bs=$BLKSZ of=${TMPFILE} 2>&1 \
| grep --line-buffered -E '[[:digit:]]* bytes' \
| awk '{ print $1 }' >> ${PROGRESS} &
# Loop while the 'dd' exists. It would be 'more better' if we
# actually looked for the specific child ID of the running
# process by identifying which child process it was. If someone
# else is running dd, it will mess things up.
# My PID handling is dumb, it assumes you only have one running dd on
# the system, this should be fixed to just get the PID of the child
# process from the shell.
while [ $(pidof dd) -gt 1 ]; do
# PROTIP: You can sleep partial seconds (at least on linux)
sleep .5
# Force dd to update us on it's progress (which gets
# redirected to $PROGRESS file.
#
# dumb pid handling again
pkill -USR1 dd
local BYTES_THIS_CYCLE=$(tail -1 $PROGRESS)
local XFER_BLKS=$(((BYTES_THIS_CYCLE-BYTES_LAST_CYCLE)/BLKSZ))
# Don't print anything unless we've got 1 block or more.
# This allows for stdin/stderr interactions to occur
# without printing a hash erroneously.
# Also makes it possible for you to background 'scp',
# but still use the /dev/stdout trick _even_ if scp
# (inevitably) asks for a password.
#
# Fancy!
if [ $XFER_BLKS -gt 0 ]; then
printf "#%0.s" $(seq 0 $XFER_BLKS)
BYTES_LAST_CYCLE=$BYTES_THIS_CYCLE
fi
done
local SIZE=$(stat -c"%s" $TMPFILE)
local NOW=$(date +"%s")
if [ $NOW -eq 0 ]; then
NOW=1
fi
local DURATION=$(($NOW-$START))
local BYTES_PER_SECOND=$(( SIZE / DURATION ))
local KBPS=$((SIZE/DURATION/1024))
local MD5=$(md5sum $TMPFILE | awk '{ print $1 }')
# This function prints out ugly stuff suitable for eval()
# rather than a pretty string. This makes it a bit more
# flexible if you have a custom format (or dare I say, locale?)
printf "\nDURATION=%d\nBYTES=%d\nKBPS=%f\nMD5=%s\n" \
$DURATION \
$SIZE \
$KBPS \
$MD5
}
Примеры:
echo "wget"
wget -q -O /dev/stdout http://www.blah.com/somefile.zip | progress_filter
echo "tftp"
tftp -l /dev/stdout -g -r something/firmware.bin 192.168.1.1 | progress_filter
echo "scp"
scp user@192.168.1.1:~/myfile.tar /dev/stdout | progress_filter
Если вам нужно показать временную шкалу прогресса (заранее зная время показа), вы можете использовать Python следующим образом:
#!/bin/python
from time import sleep
import sys
if len(sys.argv) != 3:
print "Usage:", sys.argv[0], "<total_time>", "<progressbar_size>"
exit()
TOTTIME=float(sys.argv[1])
BARSIZE=float(sys.argv[2])
PERCRATE=100.0/TOTTIME
BARRATE=BARSIZE/TOTTIME
for i in range(int(TOTTIME)+1):
sys.stdout.write('\r')
s = "[%-"+str(int(BARSIZE))+"s] %d%% "
sys.stdout.write(s % ('='*int(BARRATE*i), int(PERCRATE*i)))
sys.stdout.flush()
SLEEPTIME = 1.0
if i == int(TOTTIME): SLEEPTIME = 0.1
sleep(SLEEPTIME)
print ""
Затем, если вы сохранили скрипт Python как progressbar.py
, можно отобразить индикатор выполнения из вашего bash-скрипта, выполнив следующую команду:
python progressbar.py 10 50
Он будет отображать индикатор выполнения размером 50
символов и «работает» в течение 10
секунд.
#!/bin/bash
tot=$(wc -c /proc/$/fd/255 | awk '/ /{print $1}')
now() {
echo $(( 100* ($(awk '/^pos:/{print $2}' < /proc/$/fdinfo/255)-166) / (tot-166) )) "%"
}
now;
now;
now;
now;
now;
now;
now;
now;
now;
вывод:
0 %
12 %
25 %
37 %
50 %
62 %
75 %
87 %
100 %
примечание: если вместо 255 вы введете 1, вы будете контролировать стандарт на ... с 2 на выходе стандарта (но вы должны изменить источник для установки " tot "до предполагаемого размера выходного файла)
Я также хотел бы добавить свой собственный индикатор выполнения
Он достигает точности субсимволов, используя половину блоков Unicode
Код включен
GNU tar имеет полезную опцию, которая дает функциональность простого индикатора выполнения.
(...) Другое доступное действие контрольной точки - «точка» (или «.»). Он указывает tar печатать одну точку в стандартном потоке листинга, например :
$ tar -c --checkpoint=1000 --checkpoint-action=dot /var
...
Тот же эффект может быть получен с помощью:
$ tar -c --checkpoint=.1000 /var
Чтобы создать индикатор выполнения tar
tar xzvf pippo.tgz |xargs -L 19 |xargs -I@ echo -n "."
Где «19» - это количество файлов в tar, разделенное на длину предполагаемого индикатора выполнения. Пример: .tgz содержит 140 файлов, и вам понадобится индикатор выполнения 76 ".", Вы можете поставить -L 2.
Вам больше ничего не нужно.
tar
, поэтому если сценарий оболочки только не состоит из tar
команда, это doesn' t действительно относятся к OP' s случай.
– doubleDown
12.01.2020, 05:59
для меня проще всего пользоваться и лучше всего выглядеть до сих пор - это команда pv
или bar
, как, например, какой-то парень уже написал
, например: нужно сделать резервную копию всего диска с помощью dd
обычно вы используете dd if="$input_drive_path" of="$output_file_path"
с pv
, вы можете сделать это следующим образом:
dd if="$input_drive_path" | pv | dd of="$output_file_path"
и прогресс переходит непосредственно к STDOUT
как это:
7.46GB 0:33:40 [3.78MB/s] [ <=> ]
после того, как это сделано, сводка появляется
15654912+0 records in
15654912+0 records out
8015314944 bytes (8.0 GB) copied, 2020.49 s, 4.0 MB/s
Ничего подобного не видел, так что ... мое очень простое решение:
#!/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"
- возврат каретки, специальный символ для возврата в начало строки Я использовал его давным-давно в простом «взломе» видео "для имитации набора текста. ;)
РЕДАКТИРОВАТЬ: Для получения обновленной версии проверьте мой github страница
Я не был удовлетворен ответами на этот вопрос. То, что я лично искал, было причудливым индикатором прогресса, как видит APT.
Я взглянул на исходный код C для APT и решил написать свой собственный эквивалент bash.
Этот индикатор выполнения будет оставаться в нижней части терминала и не будет мешать любому выводу, отправленному на терминал.
Обратите внимание, что в настоящее время полоса имеет ширину 100 символов. Если вы хотите масштабировать его до размера терминала, это также довольно легко сделать (обновленная версия на моей странице github хорошо справляется с этим).
Я опубликую свой сценарий здесь. Пример использования:
source ./progress_bar.sh
echo "This is some output"
setup_scroll_area
sleep 1
echo "This is some output 2"
draw_progress_bar 10
sleep 1
echo "This is some output 3"
draw_progress_bar 50
sleep 1
echo "This is some output 4"
draw_progress_bar 90
sleep 1
echo "This is some output 5"
destroy_scroll_area
Скрипт (я настоятельно рекомендую вместо этого версию на моем github):
#!/bin/bash
# This code was inspired by the open source C code of the APT progress bar
# http://bazaar.launchpad.net/~ubuntu-branches/ubuntu/trusty/apt/trusty/view/head:/apt-pkg/install-progress.cc#L233
#
# Usage:
# Source this script
# setup_scroll_area
# draw_progress_bar 10
# draw_progress_bar 90
# destroy_scroll_area
#
CODE_SAVE_CURSOR="\033[s"
CODE_RESTORE_CURSOR="\033[u"
CODE_CURSOR_IN_SCROLL_AREA="\033[1A"
COLOR_FG="\e[30m"
COLOR_BG="\e[42m"
RESTORE_FG="\e[39m"
RESTORE_BG="\e[49m"
function setup_scroll_area() {
lines=$(tput lines)
let lines=$lines-1
# Scroll down a bit to avoid visual glitch when the screen area shrinks by one row
echo -en "\n"
# Save cursor
echo -en "$CODE_SAVE_CURSOR"
# Set scroll region (this will place the cursor in the top left)
echo -en "\033[0;${lines}r"
# Restore cursor but ensure its inside the scrolling area
echo -en "$CODE_RESTORE_CURSOR"
echo -en "$CODE_CURSOR_IN_SCROLL_AREA"
# Start empty progress bar
draw_progress_bar 0
}
function destroy_scroll_area() {
lines=$(tput lines)
# Save cursor
echo -en "$CODE_SAVE_CURSOR"
# Set scroll region (this will place the cursor in the top left)
echo -en "\033[0;${lines}r"
# Restore cursor but ensure its inside the scrolling area
echo -en "$CODE_RESTORE_CURSOR"
echo -en "$CODE_CURSOR_IN_SCROLL_AREA"
# We are done so clear the scroll bar
clear_progress_bar
# Scroll down a bit to avoid visual glitch when the screen area grows by one row
echo -en "\n\n"
}
function draw_progress_bar() {
percentage=$1
lines=$(tput lines)
let lines=$lines
# Save cursor
echo -en "$CODE_SAVE_CURSOR"
# Move cursor position to last row
echo -en "\033[${lines};0f"
# Clear progress bar
tput el
# Draw progress bar
print_bar_text $percentage
# Restore cursor position
echo -en "$CODE_RESTORE_CURSOR"
}
function clear_progress_bar() {
lines=$(tput lines)
let lines=$lines
# Save cursor
echo -en "$CODE_SAVE_CURSOR"
# Move cursor position to last row
echo -en "\033[${lines};0f"
# clear progress bar
tput el
# Restore cursor position
echo -en "$CODE_RESTORE_CURSOR"
}
function print_bar_text() {
local percentage=$1
# Prepare progress bar
let remainder=100-$percentage
progress_bar=$(echo -ne "["; echo -en "${COLOR_FG}${COLOR_BG}"; printf_new "#" $percentage; echo -en "${RESTORE_FG}${RESTORE_BG}"; printf_new "." $remainder; echo -ne "]");
# Print progress bar
if [ $1 -gt 99 ]
then
echo -ne "${progress_bar}"
else
echo -ne "${progress_bar}"
fi
}
printf_new() {
str=$1
num=$2
v=$(printf "%-${num}s" "$str")
echo -ne "${v// /$str}"
}
У меня было то же самое сегодня, и, основываясь на ответе Диомидиса, вот как я это сделал (linux debian 6.0.7). Может быть, это могло бы помочь вам:
#!/bin/bash
echo "getting script inode"
inode=`ls -i ./script.sh | cut -d" " -f1`
echo $inode
echo "getting the script size"
size=`cat script.sh | wc -c`
echo $size
echo "executing script"
./script.sh &
pid=$!
echo "child pid = $pid"
while true; do
let offset=`lsof -o0 -o -p $pid | grep $inode | awk -F" " '{print $7}' | cut -d"t" -f 2`
let percent=100*$offset/$size
echo -ne " $percent %\r"
done
progressbar: Zeile 17: let: offset=: Syntax Fehler: Operator erwartet. (Fehlerverursachendes Zeichen ist \"=\").
, если я называю этот сценарий progressbar.sh
и называю его с cd /tmp/; echo "sleep 5">script.sh; bash progressbar.sh
на Ubuntu 13.04
– rubo77
12.01.2020, 06:01
lsof: WARNING: can't stat() fuse.gvfsd-fuse file system /home/rubo77/.gvfs Output information may be incomplete.
– rubo77
12.01.2020, 06:01
Сначала запустите процесс в фоновом режиме, затем часто наблюдайте за его текущим состоянием, который выполнялся, распечатайте шаблон и еще раз проверьте, выполнялось ли это состояние или нет;
Использование цикла while для просмотра состояния процесса часто.
используйте pgrep или любую другую команду для просмотра и получения статуса выполнения процесса.
при использовании pgrep перенаправьте ненужный вывод в / dev / null по мере необходимости.
Код:
sleep 12&
while pgrep sleep &> /dev/null;do echo -en "#";sleep 0.5;done
Этот «#» будет печататься до тех пор, пока не прекратится спящий режим, этот метод используется для реализации индикатора выполнения для времени выполнения программы.
Вы также можете использовать этот метод для команд для обработки сценариев, чтобы проанализировать его время обработки как визуальное.
BUG: этот метод pgrep работает не во всех ситуациях, неожиданно другой процесс выполнялся с тем же именем, а цикл while не заканчивается.
, чтобы получить статус выполнения процесса, указав его PID, используя, возможно, процесс, который может быть доступен с некоторыми командами,
команда пс перечислит все процесс с идентификатором, вам нужно grep , чтобы узнать pid указанного процесса
#!/bin/bash
function progress_bar() {
bar=""
total=10
[[ -z $1 ]] && input=0 || input=${1}
x="##"
for i in `seq 1 10`; do
if [ $i -le $input ] ;then
bar=$bar$x
else
bar="$bar "
fi
done
#pct=$((200*$input/$total % 2 + 100*$input/$total))
pct=$(($input*10))
echo -ne "Progress : [ ${bar} ] (${pct}%) \r"
sleep 1
if [ $input -eq 10 ] ;then
echo -ne '\n'
fi
}
может создать функцию, которая рисует это в масштабе, скажем, 1-10 для количества баров:
progress_bar 1
echo "doing something ..."
progress_bar 2
echo "doing something ..."
progress_bar 3
echo "doing something ..."
progress_bar 8
echo "doing something ..."
progress_bar 10
Чтобы указать ход выполнения действий, попробуйте следующие команды:
while true; do sleep 0.25 && echo -ne "\r\\" && sleep 0.25 && echo -ne "\r|" && sleep 0.25 && echo -ne "\r/" && sleep 0.25 && echo -ne "\r-"; done;
ИЛИ
while true; do sleep 0.25 && echo -ne "\rActivity: \\" && sleep 0.25 && echo -ne "\rActivity: |" && sleep 0.25 && echo -ne "\rActivity: /" && sleep 0.25 && echo -ne "\rActivity: -"; done;
ИЛИ
while true; do sleep 0.25 && echo -ne "\r" && sleep 0.25 && echo -ne "\r>" && sleep 0.25 && echo -ne "\r>>" && sleep 0.25 && echo -ne "\r>>>"; sleep 0.25 && echo -ne "\r>>>>"; done;
ИЛИ
while true; do sleep .25 && echo -ne "\r:Active:" && sleep .25 && echo -ne "\r:aCtive:" && sleep .25 && echo -ne "\r:acTive:" && sleep .25 && echo -ne "\r:actIve:" && sleep .25 && echo -ne "\r:actiVe:" && sleep .25 && echo -ne "\r:activE:"; done;
Можно использовать флаги / переменные внутри цикла while для проверки и отображения значения / степени прогресса.
Основываясь на работе Эдуарда Лопеса, я создал индикатор выполнения, который соответствует размеру экрана, каким бы он ни был. Проверьте это.
Он также размещен на Git Hub .
#!/bin/bash
#
# Progress bar by Adriano Pinaffo
# Available at https://github.com/adriano-pinaffo/progressbar.sh
# Inspired on work by Edouard Lopez (https://github.com/edouard-lopez/progress-bar.sh)
# Version 1.0
# Date April, 28th 2017
function error {
echo "Usage: [110] [SECONDS]"
case $1 in
1) echo "Pass one argument only"
exit 1
;;
2) echo "Parameter must be a number"
exit 2
;;
*) echo "Unknown error"
exit 999
esac
}
[[ $# -ne 1 ]] && error 1
[[ $1 =~ ^[0-9]+$ ]] || error 2
duration=${1}
barsize=$((`tput cols` - 7))
unity=$(($barsize / $duration))
increment=$(($barsize%$duration))
skip=$(($duration/($duration-$increment)))
curr_bar=0
prev_bar=
for (( elapsed=1; elapsed<=$duration; elapsed++ ))
do
# Elapsed
prev_bar=$curr_bar
let curr_bar+=$unity
[[ $increment -eq 0 ]] || {
[[ $skip -eq 1 ]] &&
{ [[ $(($elapsed%($duration/$increment))) -eq 0 ]] && let curr_bar++; } ||
{ [[ $(($elapsed%$skip)) -ne 0 ]] && let curr_bar++; }
}
[[ $elapsed -eq 1 && $increment -eq 1 && $skip -ne 1 ]] && let curr_bar++
[[ $(($barsize-$curr_bar)) -eq 1 ]] && let curr_bar++
[[ $curr_bar -lt $barsize ]] || curr_bar=$barsize
for (( filled=0; filled<=$curr_bar; filled++ )); do
printf "▇"
done
# Remaining
for (( remain=$curr_bar; remain<$barsize; remain++ )); do
printf " "
done
# Percentage
printf "| %s%%" $(( ($elapsed*100)/$duration))
# Return
sleep 1
printf "\r"
done
printf "\n"
exit 0
Наслаждайтесь
Используя предложения, перечисленные выше, я решил реализовать свой собственный индикатор выполнения.
#!/usr/bin/env bash
main() {
for (( i = 0; i <= 100; i=$i + 1)); do
progress_bar "$i"
sleep 0.1;
done
progress_bar "done"
exit 0
}
progress_bar() {
if [ "$1" == "done" ]; then
spinner="X"
percent_done="100"
progress_message="Done!"
new_line="\n"
else
spinner='/-\|'
percent_done="${1:-0}"
progress_message="$percent_done %"
fi
percent_none="$(( 100 - $percent_done ))"
[ "$percent_done" -gt 0 ] && local done_bar="$(printf '#%.0s' $(seq -s ' ' 1 $percent_done))"
[ "$percent_none" -gt 0 ] && local none_bar="$(printf '~%.0s' $(seq -s ' ' 1 $percent_none))"
# print the progress bar to the screen
printf "\r Progress: [%s%s] %s %s${new_line}" \
"$done_bar" \
"$none_bar" \
"${spinner:x++%${#spinner}:1}" \
"$progress_message"
}
main "$@"
Я основывался на ответе, предоставленном страхом
. Он соединяется с базой данных Oracle для получения информации о ходе восстановления RMAN.
#!/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- Я основывался на ответе, предоставленном страхом
. Он соединяется с базой данных Oracle для получения информации о ходе восстановления RMAN.
[110]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}%%"
}
function rman_check {
sqlplus -s / as sysdba <<EOF
set heading off
set feedback off
select
round((sofar/totalwork) * 100,0) pct_done
from
v\$session_longops
where
totalwork > sofar
AND
opname NOT LIKE '%aggregate%'
AND
opname like 'RMAN%';
exit
EOF
}
# Variables
_start=1
# This accounts as the "totalState" variable for the ProgressBar function
_end=100
_rman_progress=$(rman_check)
#echo ${_rman_progress}
# Proof of concept
#for number in $(seq ${_start} ${_end})
while [ ${_rman_progress} -lt 100 ]
do
for number in _rman_progress
do
sleep 10
ProgressBar ${number} ${_end}
done
_rman_progress=$(rman_check)
done
printf '\nFinished!\n'
Это психоделический индикатор прогресса для bash-скриптинга от nExace. Его можно вызвать из командной строки как «./progressbar x y», где «x» - это время в секундах, а «y» - это сообщение, связанное с этой частью процесса.
Внутренняя функция progressbar () сама по себе также хороша, если вы хотите, чтобы другие части вашего скрипта управляли индикатором выполнения. Например, отправив «Индикатор прогресса 10« Создание дерева каталогов »; будет отображаться:
[####### ] (10%) Creating directory tree
Конечно, это будет приятно психоделический, хотя ...
#!/bin/bash
if [ "$#" -eq 0 ]; then echo "x is \"time in seconds\" and z is \"message\""; echo "Usage: progressbar x z"; exit; fi
progressbar() {
local loca=$1; local loca2=$2;
declare -a bgcolors; declare -a fgcolors;
for i in {40..46} {100..106}; do
bgcolors+=("$i")
done
for i in {30..36} {90..96}; do
fgcolors+=("$i")
done
local u=$(( 50 - loca ));
local y; local t;
local z; z=$(printf '%*s' "$u");
local w=$(( loca * 2 ));
local bouncer=".oO°Oo.";
for ((i=0;i<loca;i++)); do
t="${bouncer:((i%${#bouncer})):1}"
bgcolor="\\E[${bgcolors[RANDOM % 14]}m \\033[m"
y+="$bgcolor";
done
fgcolor="\\E[${fgcolors[RANDOM % 14]}m"
echo -ne " $fgcolor$t$y$z$fgcolor$t \\E[96m(\\E[36m$w%\\E[96m)\\E[92m $fgcolor$loca2\\033[m\r"
};
timeprogress() {
local loca="$1"; local loca2="$2";
loca=$(bc -l <<< scale=2\;"$loca/50")
for i in {1..50}; do
progressbar "$i" "$loca2";
sleep "$loca";
done
printf "\n"
};
timeprogress "$1" "$2"
Я хотел отследить прогресс, основываясь на количестве строк, которые выводила команда, против целевого числа строк из предыдущего запуска:
#!/bin/bash
function lines {
local file=$1
local default=$2
if [[ -f $file ]]; then
wc -l $file | awk '{print $1}';
else
echo $default
fi
}
function bar {
local items=$1
local total=$2
local size=$3
percent=$(($items*$size/$total % $size))
left=$(($size-$percent))
chars=$(local s=$(printf "%${percent}s"); echo "${s// /=}")
echo -ne "[$chars>";
printf "%${left}s"
echo -ne ']\r'
}
function clearbar {
local size=$1
printf " %${size}s "
echo -ne "\r"
}
function progress {
local pid=$1
local total=$2
local file=$3
bar 0 100 50
while [[ "$(ps a | awk '{print $1}' | grep $pid)" ]]; do
bar $(lines $file 0) $total 50
sleep 1
done
clearbar 50
wait $pid
return $?
}
Затем используйте его так:
target=$(lines build.log 1000)
(mvn clean install > build.log 2>&1) &
progress $! $target build.log
Он выводит индикатор выполнения, который выглядит примерно так:
[===============================================> ]
Индикатор растет по мере того, как количество выводимых строк достигает цели. Если количество строк превышает цель, планка начинается снова (надеюсь, цель хорошая).
Кстати: я использую bash на Mac OSX. Я основал этот код на счетчике из Мариасио .
Однажды у меня также был занят сценарий, который был занят часами, не показывая никакого прогресса. Поэтому я реализовал функцию, которая в основном включает в себя приемы предыдущих ответов:
#!/bin/bash
# Updates the progress bar
# Parameters: 1. Percentage value
update_progress_bar()
{
if [ $# -eq 1 ];
then
if [[ $1 == [0-9]* ]];
then
if [ $1 -ge 0 ];
then
if [ $1 -le 100 ];
then
local val=$1
local max=100
echo -n "["
for j in $(seq $max);
do
if [ $j -lt $val ];
then
echo -n "="
else
if [ $j -eq $max ];
then
echo -n "]"
else
echo -n "."
fi
fi
done
echo -ne " "$val"%\r"
if [ $val -eq $max ];
then
echo ""
fi
fi
fi
fi
fi
}
update_progress_bar 0
# Further (time intensive) actions and progress bar updates
update_progress_bar 100
Я использовал ответ из Создание строки повторяющихся символов в скрипте оболочки для повторения символов. У меня есть две относительно небольшие версии bash для сценариев, которые должны отображать индикатор выполнения (например, цикл, проходящий по многим файлам, но бесполезный для больших файлов tar или операций копирования). Более быстрая состоит из двух функций: одна для подготовки строк к отображению строки:
preparebar() {
# $1 - bar length
# $2 - bar char
barlen=$1
barspaces=$(printf "%*s" "$1")
barchars=$(printf "%*s" "$1" | tr ' ' "$2")
}
и одна для отображения индикатора выполнения:
progressbar() {
# $1 - number (-1 for clearing the bar)
# $2 - max number
if [ $1 -eq -1 ]; then
printf "\r $barspaces\r"
else
barch=$(($1*barlen/$2))
barsp=$((barlen-barch))
printf "\r[%.${barch}s%.${barsp}s]\r" "$barchars" "$barspaces"
fi
}
Его можно использовать как:
preparebar 50 "#"
, что означает подготовку строк для строки с 50 символами "#", и после этого:
progressbar 35 80
отобразит количество символов "#", соответствующее соотношению 35/80 :
[##################### ]
Имейте в виду, что функция отображает полосу на одной и той же строке снова и снова, пока вы (или какая-либо другая программа) не напечатаете новую строку. Если в качестве первого параметра указать -1, полоса будет удалена:
progressbar -1 80
Более медленная версия все в одной функции:
progressbar() {
# $1 - number
# $2 - max number
# $3 - number of '#' characters
if [ $1 -eq -1 ]; then
printf "\r %*s\r" "$3"
else
i=$(($1*$3/$2))
j=$(($3-i))
printf "\r[%*s" "$i" | tr ' ' '#'
printf "%*s]\r" "$j"
fi
}
и ее можно использовать как ( тот же пример, что и выше):
progressbar 35 80 50
Если вам нужен прогрессбар на stderr, просто добавьте >&2
в конце каждой команды printf.
Многие ответы описывают написание ваших собственных команд для распечатки '\r' + $some_sort_of_progress_msg
. Иногда проблема заключается в том, что распечатка сотен этих обновлений в секунду замедлит процесс.
Однако, если какой-либо из ваших процессов выдает результат (например, 7z a -r newZipFile myFolder
будет выводить каждое имя файла при его сжатии), тогда существует более простое, быстрое, безболезненное и настраиваемое решение.
Установите модуль Python tqdm
.
$ sudo pip install tqdm
$ # now have fun
$ 7z a -r -bd newZipFile myFolder | tqdm >> /dev/null
$ # if we know the expected total, we can have a bar!
$ 7z a -r -bd newZipFile myFolder | grep -o Compressing | tqdm --total $(find myFolder -type f | wc -l) >> /dev/null
Справка: tqdm -h
. Пример использования дополнительных опций:
$ find / -name '*.py' -exec cat \{} \; | tqdm --unit loc --unit_scale True | wc -l
В качестве бонуса вы также можете использовать tqdm
, чтобы обернуть итерации в коде Python.
Это применимо только с использованием гнома zenity. Zenity предоставляет отличный нативный интерфейс для скриптов bash: https://help.gnome.org/users/zenity/stable/
Из строки прогресса Zenity Пример:
#!/bin/sh
(
echo "10" ; sleep 1
echo "# Updating mail logs" ; sleep 1
echo "20" ; sleep 1
echo "# Resetting cron jobs" ; sleep 1
echo "50" ; sleep 1
echo "This line will just be ignored" ; sleep 1
echo "75" ; sleep 1
echo "# Rebooting system" ; sleep 1
echo "100" ; sleep 1
) |
zenity --progress \
--title="Update System Logs" \
--text="Scanning mail logs..." \
--percentage=0
if [ "$?" = -1 ] ; then
zenity --error \
--text="Update canceled."
fi
используйте команду linux pv:
, если размер неизвестен, он находится в середине потока, но он дает скорость и общее количество, и оттуда вы можете выяснить, сколько времени это займет, и получить обратную связь, чтобы вы знали, что он не завис.
Получил простую функцию индикатора выполнения, которую я написал на днях:
#!/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- Получил простую функцию индикатора выполнения, которую я написал на днях:
[110] Или поймал ее,
https://github.com/fearside/ProgressBar /
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 /
Я искал что-то более сексуальное, чем выбранный ответ, как и мой собственный сценарий.
Я поместил его на 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
Некоторые посты показали, как отображать прогресс команды. Чтобы рассчитать это, вам нужно увидеть, насколько вы продвинулись. В системах 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 появилась в моем блоге .
Большинство команд Unix не даст вам прямой обратной связи, из которой вы можете сделать это. Некоторые дадут вам вывод на стандартный вывод или стандартный вывод, который вы можете использовать.
Для чего-то вроде tar вы можете использовать ключ -v и направить вывод в программу, которая обновляет небольшую анимацию для каждой строки, которую она читает. Когда tar записывает список файлов, которые он распаковывает, программа может обновить анимацию. Чтобы выполнить процент выполнения, вам нужно знать количество файлов и считать строки.
cp не дает такой вывод, насколько я знаю. Чтобы отслеживать прогресс cp, вам нужно будет отслеживать исходные и конечные файлы и следить за размером получателя. Вы можете написать небольшую программу на c, используя системный вызов stat (2) , чтобы получить размер файла. Это будет считывать размер источника, затем опрашивать файл назначения и обновлять панель% complete в зависимости от размера файла, записанного на сегодняшний день.
printf
вместоecho
. – Jens 12.01.2020, 05:55printf "#### (50%%)\r"
, это wouldn' t работа с одинарными кавычками и знаком процента должен быть оставлен. – nurettin 12.01.2020, 05:55