在bash或* NIX中的任何其他shell中编写脚本时,运行一个需要几秒钟的命令时,需要一个进度条。
例如,复制一个大文件,打开一个大的tar文件。
您建议将哪些方法添加到shell脚本?
答案 0 :(得分:646)
您可以通过覆盖一行来实现此目的。使用\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提到这个“失败”,如果你从一个长行开始然后想写一个短行:在这种情况下,你需要覆盖长行的长度(例如,空格)。
答案 1 :(得分:60)
答案 2 :(得分:48)
有些帖子展示了如何显示命令的进度。为了计算它,你需要看看你进步了多少。在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 shell脚本出现在my blog。
答案 3 :(得分:40)
使用linux命令pv:
它不知道大小是否在流的中间,但是它给出了一个速度和总和,从那里你可以弄清楚应该花多长时间并获得反馈,这样你就知道它没有挂起。
答案 4 :(得分:36)
我前几天写了一个简单的进度条功能:
#!/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'
答案 5 :(得分:22)
我一直在寻找比选定答案更性感的东西,我自己的剧本也是如此。
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
答案 6 :(得分:17)
GNU tar有一个有用的选项,它提供了一个简单进度条的功能。
(...)另一个可用的检查点操作是'dot'(或'。')。它指示tar在标准列表流上打印单个点,例如:
$ tar -c --checkpoint=1000 --checkpoint-action=dot /var
...
可以通过以下方式获得相同的效果:
$ tar -c --checkpoint=.1000 /var
答案 7 :(得分:12)
答案 8 :(得分:11)
答案 9 :(得分:7)
这使您可以看到命令仍在执行:
while :;do echo -n .;sleep 1;done &
trap "kill $!" EXIT #Die with parent if we die prematurely
tar zxf packages.tar.gz; # or any other command here
kill $! && trap " " EXIT #Kill the loop and unset the trap or else the pid might get reassigned and we might end up killing a completely different process
这将创建一个无限循环,它在后台执行并回显“。”每一秒。这将在shell中显示.
。运行tar
命令或任何您想要的命令。当该命令完成执行后, kill 在后台运行的最后一个作业 - 这是无限循环。
答案 10 :(得分:6)
没有看到类似的东西......我非常简单的解决方案:
Location Company
1 New York, NY XYZ Company
2 Chicago, IL Consulting Firm
3 Miami, FL Smith
#!/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
- 回车,返回行首的特殊字符我很久以前在一个简单的“黑客视频”中使用它来模拟输入代码。 ;)
答案 11 :(得分:4)
首先,酒吧不是唯一一个管道进度表。另一个(可能更为人所知)是pv(管道查看器)。
其次bar和pv可以用作例如:
$ bar file1 | wc -l
$ pv file1 | wc -l
甚至:
$ tail -n 100 file1 | bar | wc -l
$ tail -n 100 file1 | pv | wc -l
一个有用的技巧,如果你想在使用参数中给出的文件的命令中使用bar和pv,例如复制file1 file2,是使用process substitution:
$ copy <(bar file1) file2
$ copy <(pv file1) file2
进程替换是一个bash魔术,它通过这个管道创建临时的fifo管道文件/ dev / fd /并从运行的进程(括号内)连接stdout,并且副本看起来就像普通文件一样(有一个例外,它可以只读它转发)。
更新
bar命令本身也允许复制。男人吧:
bar --in-file /dev/rmt/1cbn --out-file \
tape-restore.tar --size 2.4g --buffer-size 64k
但在我看来,流程替换是更通用的方式。它使用cp程序本身。
答案 12 :(得分:4)
大多数unix命令都不会为您提供直接反馈,您可以从中执行此操作。 有些人会在stdout或stderr上为你提供输出。
对于类似tar的东西,你可以使用-v开关并将输出传递给一个程序,该程序为它读取的每一行更新一个小动画。当tar写出一个文件列表时,它会被解开,程序可以更新动画。要完成百分比,您必须知道文件的数量并计算行数。
据我所知,cp并没有提供这种输出。要监视cp的进度,您必须监视源文件和目标文件并观察目标的大小。您可以使用stat (2)系统调用编写一个小型c程序来获取文件大小。这将读取源的大小,然后轮询目标文件并根据迄今为止写入的文件大小更新%完成栏。
答案 13 :(得分:4)
我的解决方案显示了tarball的百分比
目前正在进行解压缩和编写。我用这个
在写出2GB根文件系统映像时。你真的
这些东西需要一个进度条。我做的是使用
gzip --list
获取总的未压缩大小
压缩包。由此我计算出所需的阻塞因子
将文件分成100个部分。最后,我打印一个
每个块的检查点消息。对于2GB文件这个
一块大约10MB。如果那太大你就可以
将BLOCKING_FACTOR除以10或100,但接着是
更难按百分比打印漂亮的输出。
假设您正在使用Bash,那么您可以使用 以下shell函数
untar_progress ()
{
TARBALL=$1
BLOCKING_FACTOR=$(gzip --list ${TARBALL} |
perl -MPOSIX -ane '$.==2 && print ceil $F[1]/50688')
tar --blocking-factor=${BLOCKING_FACTOR} --checkpoint=1 \
--checkpoint-action='ttyout=Wrote %u% \r' -zxf ${TARBALL}
}
答案 14 :(得分:3)
编辑:有关更新版本,请检查我的github page
我对这个问题的回答不满意。我个人想要的是APT看到的花哨的进度条。
我查看了APT的C源代码,并决定为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}"
}
答案 15 :(得分:3)
我更喜欢将对话框与 - gauge 参数一起使用。经常在.deb软件包安装和许多发行版的其他基本配置中使用。所以你不需要再次重新发明轮子
只需将1到100之间的int值放入@stdin。一个基本而愚蠢的例子:
for a in {1..100}; do sleep .1s; echo $a| dialog --gauge "waiting" 7 30; done
我有这个 / bin / Wait 文件(chmod u + x perms)用于烹饪目的:P
#!/bin/bash
INIT=`/bin/date +%s`
NOW=$INIT
FUTURE=`/bin/date -d "$1" +%s`
[ $FUTURE -a $FUTURE -eq $FUTURE ] || exit
DIFF=`echo "$FUTURE - $INIT"|bc -l`
while [ $INIT -le $FUTURE -a $NOW -lt $FUTURE ]; do
NOW=`/bin/date +%s`
STEP=`echo "$NOW - $INIT"|bc -l`
SLEFT=`echo "$FUTURE - $NOW"|bc -l`
MLEFT=`echo "scale=2;$SLEFT/60"|bc -l`
TEXT="$SLEFT seconds left ($MLEFT minutes)";
TITLE="Waiting $1: $2"
sleep 1s
PTG=`echo "scale=0;$STEP * 100 / $DIFF"|bc -l`
echo $PTG| dialog --title "$TITLE" --gauge "$TEXT" 7 72
done
if [ "$2" == "" ]; then msg="Espera terminada: $1";audio="Listo";
else msg=$2;audio=$2;fi
/usr/bin/notify-send --icon=stock_appointment-reminder-excl "$msg"
espeak -v spanish "$audio"
所以我可以说:
Wait "34 min" "warm up the oven"
或
Wait "dec 31" "happy new year"
答案 16 :(得分:2)
抱歉这个不是那么短的答案。
对于不耐烦的人:请在现在做吧!(在中间)测试代码,最后动画演示(在此底部。)
此处的所有演示都使用 read -t <float seconds> && break
而不是 sleep
。所以所有的循环都可以通过点击 Return 键很好地停止。
又一个 Bash 进度条...
这里已经有很多答案了,我想补充一些关于性能和精度.
因为进度条旨在在其他进程正在运行时运行,所以这一定是一个不错进程...
因此避免在不需要时使用叉。示例:代替
mysmiley=$(printf '%b' \\U1F60E)
使用
printf -v mysmiley '%b' \\U1F60E
说明:当您运行 var=$(command)
时,您启动了一个新进程来执行 command
并将他的输出发送到变量 { {1}} 一旦终止。这是非常资源昂贵的。请比较:
$var
在我的主机上,分配 TIMEFORMAT="%R"
time for ((i=2500;i--;)){ mysmiley=$(printf '%b' \\U1F60E);}
2.292
time for ((i=2500;i--;)){ printf -v mysmiley '%b' \\U1F60E;}
0.017
bc -l <<<'2.292/.017'
134.82352941176470588235
的相同工作(仅 2500 次),使用 fork 比使用内置 $mysmiley
慢 135 倍/更贵.
然后
printf -v
所以你的 echo $mysmiley
?
必须不打印(或输出)任何东西。您的函数必须将他的答案归因于一个变量。
这是一个非常小而快速的函数,用于从整数计算百分比,使用整数并回答一个伪浮点数:
function
用法:
percent(){
local p=00$(($1*100000/$2))
printf -v "$3" %.2f ${p::-3}.${p: -3}
}
# percent <integer to compare> <reference integer> <variable name>
percent 33333 50000 testvar
printf '%8s%%\n' "$testvar"
66.67%
要使用 bash 渲染这些字符,您可以:
▏ ▎ ▍ ▌ ▋ ▊ ▉ █
那么我们必须使用 8x printf -v chars '\\U258%X ' {15..8}
printf "$chars\\n"
▏ ▎ ▍ ▌ ▋ ▊ ▉ █
作为 string with
。
这个函数被命名为 graphic width
是因为它根据以百分比(浮动)提交的参数呈现一个条形:
percentBar
用法:
percentBar () {
local prct totlen=$((8*$2)) lastchar barstring blankstring;
printf -v prct %.2f "$1"
((prct=10#${prct/.}*totlen/10000, prct%8)) &&
printf -v lastchar '\\U258%X' $(( 16 - prct%8 )) ||
lastchar=''
printf -v barstring '%*s' $((prct/8)) ''
printf -v barstring '%b' "${barstring// /\\U2588}$lastchar"
printf -v blankstring '%*s' $(((totlen-prct)/8)) ''
printf -v "$3" '%s%s' "$barstring" "$blankstring"
}
为了显示细微差别:
# percentBar <float percent> <int string width> <variable name>
percentBar 42.42 $COLUMNS bar1
echo "$bar1"
█████████████████████████████████▉
由于渲染变量是一个固定宽度的字符串,使用颜色很容易:
percentBar 42.24 $COLUMNS bar2
printf "%s\n" "$bar1" "$bar2"
█████████████████████████████████▉
█████████████████████████████████▊
percentBar 72.1 24 bar
printf 'Show this: \e[44;33;1m%s\e[0m at %s%%\n' "$bar" 72.1
另一个展示不同尺寸和彩色输出的演示:
for i in {0..10000..33} 10000;do i=0$i
printf -v p %0.2f ${i::-2}.${i: -2}
percentBar $p $((COLUMNS-9)) bar
printf '\r|%s|%6.2f%%' "$bar" $p
read -srt .002 _ && break # console sleep avoiding fork
done
|███████████████████████████████████████████████████████████████████████|100.00%
clear; for i in {0..10000..33} 10000;do i=0$i
printf -v p %0.2f ${i::-2}.${i: -2}
percentBar $p $((COLUMNS-7)) bar
printf '\r\e[47;30m%s\e[0m%6.2f%%' "$bar" $p
read -srt .002 _ && break
done
可以产生这样的东西:
答案 17 :(得分:2)
可以通过非常简单的方式实现:
for
循环从0到100重复$bar
变量后附加另一个=
符号,以使进度条更宽\r
清除行并返回到行的开头; -ne
使echo
不在末尾添加换行符并解析{{1} }特殊字符)\r
function progress {
bar=''
for (( x=0; x <= 100; x++ )); do
sleep 0.25
bar="${bar}="
echo -ne "$bar ${x}%\r"
done
echo -e "\n"
}
$ progress
> ========== 10% # here: after 2.5 seconds
彩色进度条
$ progress
> ============================== 30% # here: after 7.5 seconds
要使进度条变彩色,您可以使用格式转义序列-这里的进度条为黄色:function progress {
bar=''
for (( x=0; x <= 100; x++ )); do
sleep 0.05
bar="${bar} "
echo -ne "\r"
echo -ne "\e[43m$bar\e[0m"
local left="$(( 100 - $x ))"
printf " %${left}s"
echo -n "${x}%"
done
echo -e "\n"
}
,然后我们用\e[43m
重设自定义设置,否则即使在以下情况下也会影响进一步的输入进度条完成了。
答案 18 :(得分:2)
上传文件
[##################################################] 100% (137921 / 137921 bytes)
等待工作完成
[######################### ] 50% (15 / 30 seconds)
您可以将其复制粘贴到脚本中。不需要其他任何操作。
PROGRESS_BAR_WIDTH=50 # progress bar length in characters
draw_progress_bar() {
# Arguments: current value, max value, unit of measurement (optional)
local __value=$1
local __max=$2
local __unit=${3:-""} # if unit is not supplied, do not display it
# Calculate percentage
if (( $__max < 1 )); then __max=1; fi # anti zero division protection
local __percentage=$(( 100 - ($__max*100 - $__value*100) / $__max ))
# Rescale the bar according to the progress bar width
local __num_bar=$(( $__percentage * $PROGRESS_BAR_WIDTH / 100 ))
# Draw progress bar
printf "["
for b in $(seq 1 $__num_bar); do printf "#"; done
for s in $(seq 1 $(( $PROGRESS_BAR_WIDTH - $__num_bar ))); do printf " "; done
printf "] $__percentage%% ($__value / $__max $__unit)\r"
}
在这里,我们上传文件,并在每次迭代时重新绘制进度条。只要获得两个值:最大值和当前值,实际上执行什么作业都没有关系。
在下面的示例中,最大值为file_size
,当前值由某些函数提供,称为uploaded_bytes
。
# Uploading a file
file_size=137921
while true; do
# Get current value of uploaded bytes
uploaded_bytes=$(some_function_that_reports_progress)
# Draw a progress bar
draw_progress_bar $uploaded_bytes $file_size "bytes"
# Check if we reached 100%
if [ $uploaded_bytes == $file_size ]; then break; fi
sleep 1 # Wait before redrawing
done
# Go to the newline at the end of upload
printf "\n"
答案 19 :(得分:2)
我需要一个进度条来迭代csv文件中的行。能够将cprn的代码改编成对我有用的东西:
BAR='##############################'
FILL='------------------------------'
totalLines=$(wc -l $file | awk '{print $1}') # num. lines in file
barLen=30
# --- iterate over lines in csv file ---
count=0
while IFS=, read -r _ col1 col2 col3; do
# update progress bar
count=$(($count + 1))
percent=$((($count * 100 / $totalLines * 100) / 100))
i=$(($percent * $barLen / 100))
echo -ne "\r[${BAR:0:$i}${FILL:$i:barLen}] $count/$totalLines ($percent%)"
# other stuff
(...)
done <$file
看起来像这样:
[##----------------------------] 17128/218210 (7%)
答案 20 :(得分:2)
许多答案描述了编写自己的命令以打印'\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代码中包装iterables。
答案 21 :(得分:2)
对我来说最容易使用,到目前为止最好看的是命令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
答案 22 :(得分:1)
我使用Creating string of repeated characters in shell script的答案来重复字符。对于需要显示进度条的脚本,我有两个相对较小的 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&#34;#&#34;字符,然后:
progressbar 35 80
将显示&#34;#&#34;对应于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上使用progressbar,只需在每个printf命令的末尾添加>&2
即可。
答案 23 :(得分:1)
我需要一个适合弹出气泡消息 (notify-send
) 的进度条来表示电视音量级别。最近在用python写一个音乐播放器,电视画面大部分时间都是关闭的。
#!/bin/bash
# Show a progress bar at step number $1 (from 0 to 100)
function is_int() { test "$@" -eq "$@" 2> /dev/null; }
# Parameter 1 must be integer
if ! is_int "$1" ; then
echo "Not an integer: ${1}"
exit 1
fi
# Parameter 1 must be >= 0 and <= 100
if [ "$1" -ge 0 ] && [ "$1" -le 100 ] 2>/dev/null
then
:
else
echo bad volume: ${1}
exit 1
fi
# Main function designed for quickly copying to another program
Main () {
Bar="" # Progress Bar / Volume level
Len=25 # Length of Progress Bar / Volume level
Div=4 # Divisor into Volume for # of blocks
Fill="▒" # Fill up to $Len
Arr=( "▉" "▎" "▌" "▊" ) # UTF-8 left blocks: 7/8, 1/4, 1/2, 3/4
FullBlock=$((${1} / Div)) # Number of full blocks
PartBlock=$((${1} % Div)) # Size of partial block (array index)
while [[ $FullBlock -gt 0 ]]; do
Bar="$Bar${Arr[0]}" # Add 1 full block into Progress Bar
(( FullBlock-- )) # Decrement full blocks counter
done
# If remainder zero no partial block, else append character from array
if [[ $PartBlock -gt 0 ]]; then
Bar="$Bar${Arr[$PartBlock]}"
fi
while [[ "${#Bar}" -lt "$Len" ]]; do
Bar="$Bar$Fill" # Pad Progress Bar with fill character
done
echo Volume: "$1 $Bar"
exit 0 # Remove this line when copying into program
} # Main
Main "$@"
使用此脚本测试终端中的进度条。
#!/bin/bash
# test_progress_bar3
Main () {
tput civis # Turn off cursor
for ((i=0; i<=100; i++)); do
CurrLevel=$(./progress_bar3 "$i") # Generate progress bar 0 to 100
echo -ne "$CurrLevel"\\r # Reprint overtop same line
sleep .04
done
echo -e \\n # Advance line to keep last progress
echo "$0 Done"
tput cnorm # Turn cursor back on
} # Main
Main "$@"
本节详细介绍了如何使用 notify-send
将垃圾弹出气泡消息快速发送到桌面。这是必需的,因为音量级别每秒可以更改多次,并且默认的气泡消息行为是让消息在桌面上停留数秒。
从上面的脚本中,main
函数被复制到名为 VolumeBar
的现有 bash 脚本中名为 tvpowered
的新函数。复制的 exit 0
函数中的 main
命令已删除。
以下是如何调用它并让 Ubuntu 的 notify-send
命令知道我们将发送垃圾邮件弹出气泡消息:
VolumeBar $CurrVolume
# Ask Ubuntu: https://askubuntu.com/a/871207/307523
notify-send --urgency=critical "tvpowered" \
-h string:x-canonical-private-synchronous:volume \
--icon=/usr/share/icons/gnome/48x48/devices/audio-speakers.png \
"Volume: $CurrVolume $Bar"
这是告诉 notify-send
立即替换最后一个弹出气泡的新行:
-h string:x-canonical-private-synchronous:volume \
volume
将弹出的气泡消息分组在一起,该组中的新消息立即替换之前的消息。您可以使用 anything
而不是 volume
。
答案 24 :(得分:1)
这仅适用于gnome zenity。 Zenity为bash脚本提供了一个很棒的原生界面: 的 https://help.gnome.org/users/zenity/stable/ 强>
来自Zenity Progress Bar示例:
#!/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
答案 25 :(得分:1)
使用上面列出的建议,我决定实施自己的进度条。
#!/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 "$@"
答案 26 :(得分:1)
根据Edouard Lopez的工作,我创建了一个适合屏幕尺寸的进度条,无论它是什么。看看吧。
它也发布在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: $0 [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
享受
答案 27 :(得分:1)
要指示活动进度,请尝试以下命令:
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循环中使用标志/变量来检查和显示进度的值/范围。
答案 28 :(得分:0)
如果您必须显示时间进度条(通过事先知道显示时间),您可以按如下方式使用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
秒的“正在运行”。
答案 29 :(得分:0)
#!/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
答案 30 :(得分:0)
我为嵌入式系统做了一个纯shell版本,利用了:
/ usr / bin / dd的SIGUSR1信号处理功能。
基本上,如果发送'kill SIGUSR1 $(pid_of_running_dd_process)',它会输出 吞吐速度和转移量的摘要。
后台dd,然后定期查询更新和生成 哈希标记就像以前的老派ftp客户端一样。
使用/ dev / stdout作为非stdout友好程序的目标,例如scp
最终结果允许您进行任何文件传输操作并获得看起来像老式FTP'hash'输出的进度更新,其中您只需为每个X字节获取一个哈希标记。
这不是生产质量代码,但你明白了。我觉得它很可爱。
对于它的价值,实际字节数可能无法在散列数中正确反映 - 根据舍入问题,您可能会有更多或更少。不要将它作为测试脚本的一部分使用,它只是让人眼前一亮。而且,是的,我知道这是非常低效的 - 它是一个shell脚本,我不为它道歉。
最后提供了wget,scp和tftp的示例。它应该适用于发出数据的任何内容。确保对非stdout友好的程序使用/ dev / 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
答案 31 :(得分:0)
答案 32 :(得分:0)
#!/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 %
注意:如果您将1改为255,而不是255,则将监视标准,而将标准2输出(但是您必须修改源以将“ tot”设置为预计的输出文件大小)
答案 33 :(得分:0)
灵活的版本,具有随机的颜色,可操作的字符串和日期。
function spinner() {
local PID="$1"
local str="${2:-Processing!}"
local delay="0.1"
# tput civis # hide cursor
while ( kill -0 $PID 2>/dev/null )
do
printf "\e[38;5;$((RANDOM%257))m%s\r\e[0m" "[$(date '+%d/%m/%Y %H:%M:%S')][ ? ? ? $str ? ? ? ]"; sleep "$delay"
printf "\e[38;5;$((RANDOM%257))m%s\r\e[0m" "[$(date '+%d/%m/%Y %H:%M:%S')][ ? ? ? $str ? ? ? ]"; sleep "$delay"
printf "\e[38;5;$((RANDOM%257))m%s\r\e[0m" "[$(date '+%d/%m/%Y %H:%M:%S')][ ? ? ? $str ? ? ? ]"; sleep "$delay"
done
printf "\e[38;5;$((RANDOM%257))m%s\r\e[0m" "[$(date '+%d/%m/%Y %H:%M:%S')][ ✅ ✅ ✅ Done! ✅ ✅ ✅ ]"; sleep "$delay"
# tput cnorm # restore cursor
return 0
}
用法:
# your long running proccess pushed to the background
sleep 20 &
# spinner capture-previous-proccess-id string
spinner $! 'Working!'
输出示例:
[04/06/2020 03:22:24][ ? ? ? Seeding! ? ? ? ]
答案 34 :(得分:0)
关于此主题的答案很多,但是在使用current length / total size
方式计算文本文件操作的百分比时,例如显示ver_big_file.json
进度的百分比,我建议使用awk
为此,例如以下代码:
awk '
function bar(x){s="";i=0;while (i++ < x) s=s "#";return s}
BEGIN{
("ls -l " ARGV[1]) | getline total;
split(total,array);
total=array[5];
}
{
cur+=length($0)+1;
percent=int(cur / total * 100);
printf "LINE %s:%s %s%%\r", NR, bar(percent*.8), percent
}
END {print}' very_big_file.json | grep "keyword" | ...
这种方法非常精确,基于流,但是仅适用于文本文件。
答案 35 :(得分:0)
我建立在恐惧提供的答案上
这将连接到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-$_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'
答案 36 :(得分:-1)
我希望根据命令输出的行数与上一次运行中的目标行数来跟踪进度:
#!/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
它会输出一个如下所示的进度条:
[===============================================> ]
条形图随着输出的行数达到目标而增长。如果行数超过目标,则栏开始(希望目标是好的)。
BTW:我在Mac OSX上使用bash。我将此代码基于mariascio的微调器。
答案 37 :(得分:-1)
首先将流程执行到后台,然后经常查看它的运行状态,正在运行打印模式并再次检查状态是否正在运行;
使用while循环来经常查看进程的状态。
使用pgrep或任何其他命令来监视并获取进程的运行状态。
如果使用pgrep根据需要将不必要的输出重定向到/ dev / null。
<强>代码:强>
sleep 12&
while pgrep sleep &> /dev/null;do echo -en "#";sleep 0.5;done
这&#34;#&#34;将打印直到睡眠终止,此方法用于实现程序进度时间的进度条。
您还可以将此方法用于shell脚本的命令,以便将其处理时间视为可视化。
<强> BUG:强> 这个pgrep方法并不适用于所有情况,出乎意料的是另一个进程以相同的名称运行,而while循环并没有结束。
通过使用指定它的PID来获取进程运行状态 这个过程可以用一些命令来实现,
命令 ps a 将列出所有具有id的进程,您需要 grep 来查找指定进程的pid < / p>
答案 38 :(得分:-1)
这是nExace用于bash脚本的迷幻进度条。它可以从命令行调用为'./progressbar x y',其中'x'是以秒为单位的时间,'y'是与该部分进度相关联的消息。
如果您希望脚本的其他部分控制进度条,则内部progressbar()函数本身也是独立的。例如,发送'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"
答案 39 :(得分:-2)
制作tar进度条
tar xzvf pippo.tgz |xargs -L 19 |xargs -I@ echo -n "."
其中“19”是tar中文件的数量除以预期进度条的长度。 例如:.tgz包含140个文件,你需要一个76“的进度条。”,你可以把-L 2。
你什么都不需要。
答案 40 :(得分:-3)
有一次我也有一个繁忙的剧本,这个剧本被占用了几个小时没有显示任何进展。所以我实现了一个功能,主要包括以前答案的技巧:
#!/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
答案 41 :(得分:-4)
今天我有同样的事情要做,基于Diomidis的回答,这就是我的做法(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