How to add a progress bar to a shell script?
在bash或*nix中的任何其他shell中编写脚本时,在运行一个将花费几秒钟以上时间的命令时,需要一个进度条。
例如,复制一个大文件,打开一个大tar文件。
您建议如何向shell脚本添加进度条?
您可以通过重写一行来实现这一点。使用
当你完成前进时,写下
用
这是一个演示:
1 2 3 4 5 6 7 8 9 10 | echo -ne '##### (33%) ' sleep 1 echo -ne '############# (66%) ' sleep 1 echo -ne '####################### (100%) ' echo -ne ' ' |
在下面的注释中,puk提到,如果您从一条长线开始,然后想写一条短线,那么这个"失败":在这种情况下,您需要覆盖长线的长度(例如,用空格)。
您也可能对如何制作旋转器感兴趣:
我能在巴什做个旋转器吗?Sure!
1
2
3
4
5
6
7 i=1
sp="/-\|"
echo -n ' '
while true
do
printf"\b${sp:i++%${#sp}:1}"
doneEach time the loop iterates, it displays the next character in the sp
string, wrapping around as it reaches the end. (i is the position of
the current character to display and ${#sp} is the length of the sp
string).The \b string is replaced by a 'backspace' character. Alternatively,
you could play with
to go back to the beginning of the line.If you want it to slow down, put a sleep command inside the loop
(after the printf).A POSIX equivalent would be:
1
2
3
4
5
6 sp='/-\|'
printf ' '
while true; do
printf '\b%.1s'"$sp"
sp=${sp#?}${sp%???}
doneIf you already have a loop which does a lot of work, you can call the
following function at the beginning of each iteration to update the
spinner:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 sp="/-\|"
sc=0
spin() {
printf"\b${sp:sc++:1}"
((sc==${#sp})) && sc=0
}
endspin() {
printf"
%s
""$@"
}
until work_done; do
spin
some_work ...
done
endspin
一些帖子展示了如何显示命令的进度。为了计算它,你需要看看你已经进步了多少。在BSD系统上,一些命令,如DD(1),接受
或者,可以使用
1 2 | $ pmonitor -c gzip /home/dds/data/mysql-2015-04-01.sql.gz 58.06% |
Linux和FreeBSD shell脚本的早期版本出现在我的博客上。
使用linux命令pv:
http://linux.die.net/man/1/pv
它不知道大小,如果它在流的中间,但它提供了一个速度和总量,从那里你可以计算出需要多长时间,并得到反馈,所以你知道它没有挂起。
前几天我写了一个简单的进度条函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | #!/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" Progress : [${_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 ' Finished! ' |
或者从……开始,
https://github.com/fearside/progressbar网站/
我在寻找比所选答案更性感的东西,我自己的剧本也是。
预览来源我把它放在Github
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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" "; } for (( elapsed=1; elapsed<=$duration; elapsed++ )); do already_done; remaining; percentage sleep 1 clean_line done clean_line } |
用法
1 | progress-bar 100 |
GNU tar有一个有用的选项,它提供了一个简单进度条的功能。
(…)另一个可用的检查点操作是"dot"(或".")。它指示tar在标准列表流上打印一个点,例如:
1 2 | $ tar -c --checkpoint=1000 --checkpoint-action=dot /var ... |
同样的效果可通过以下方式获得:
1 | $ tar -c --checkpoint=.1000 /var |
使用pipeview(pv)实用程序在我的系统上工作的更简单的方法。
1 2 3 4 5 | srcdir=$1 outfile=$2 tar -Ocf - $srcdir | pv -i 1 -w 50 -berps `du -bs $srcdir | awk '{print $1}'` | 7za a -si $outfile |
我还想贡献我自己的进度条
它使用半Unicode块实现子字符精度
包括代码
这使您可以可视化命令仍在执行:
1 2 3 4 | 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 |
这将创建一个无限的while循环,该循环在后台执行并每秒回声一个"."。这将在shell中显示
没见过类似的东西,所以…我的简单解决方案:
1 2 3 4 5 6 7 | #!/bin/bash BAR='####################' # this is full bar, mine is 20 chars for i in {1..20}; do echo -ne" ${BAR:0:$i}" # print $i chars of $BAR from 0 position sleep .1 done |
echo -n 打印,末尾无新行echo -e —打印时解释特殊字符" 回车,返回行首的特殊字符
"
很久以前我在一个简单的"黑客视频"中使用它来模拟输入代码。;)
我的解决方案显示了当前正在解压缩和写入。我用这个当写出2GB根文件系统映像时。你真的这些事情需要一个进度条。我所做的就是使用
假设您使用的是bash,那么您可以使用跟随shell函数
1 2 3 4 5 6 7 8 9 | 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% ' -zxf ${TARBALL} } |
首先,酒吧不是唯一的一个管道进度表。另一个(也许更为人所知)是pv(管道查看器)。
第二个例子是bar和pv:
1 2 | $ bar file1 | wc -l $ pv file1 | wc -l |
甚至:
1 2 | $ tail -n 100 file1 | bar | wc -l $ tail -n 100 file1 | pv | wc -l |
如果要在处理参数中给定文件(如copy file1 file2)的命令中使用bar和pv,一个有用的技巧是使用进程替换:
1 2 | $ copy <(bar file1) file2 $ copy <(pv file1) file2 |
进程替换是一个bash神奇的东西,它创建临时的fifo管道文件/dev/fd/,并通过这个管道从运行的进程(在括号内)连接stdout,copy看到它就像一个普通文件一样(只有一个例外,它只能向前读取)。
更新:
BAR命令本身也允许复制。男子酒吧后:
1 2 | bar --in-file /dev/rmt/1cbn --out-file \ tape-restore.tar --size 2.4g --buffer-size 64k |
但在我看来,过程替换是更通用的方法。它使用CP程序本身。
大多数Unix命令都不会给您提供这样做的直接反馈。有些会在stdout或stderr上提供您可以使用的输出。
对于类似tar的东西,您可以使用-v开关并将输出通过管道传输到一个程序,该程序会为它读取的每一行更新一个小动画。当tar写下一个文件列表时,它会被打开,程序可以更新动画。要完成百分比,您必须知道文件的数量并计算行数。
据我所知,CP并没有给出这种输出。要监视CP的进度,您必须监视源文件和目标文件,并监视目标的大小。您可以使用stat(2)系统调用编写一个小的C程序来获取文件大小。这将读取源文件的大小,然后轮询目标文件,并根据当前写入的文件的大小更新一个完成百分比的栏。
我更喜欢使用对话框和--gauge参数。在许多发行版的.deb包安装和其他基本配置中非常常用。所以你不需要重新发明轮子…再一次
只需将int值从1到100@stdin。一个基本而愚蠢的例子:
1 | for a in {1..100}; do sleep .1s; echo $a| dialog --gauge"waiting" 7 30; done |
我有这个/bin/wait文件(带有chmod u+x烫发)用于烹饪:p
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #!/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" |
所以我可以说:
或
许多答案描述了如何编写自己的命令来打印
' + $some_sort_of_progress_msg
但是,如果您的任何进程产生输出(例如
安装python模块
1 2 3 4 5 | $ 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 |
帮助:
1 | $ find / -name '*.py' -exec cat \{} \; | tqdm --unit loc --unit_scale True | wc -l |
另外,您还可以使用
https://github.com/tqdm/tqdm/blob/master/readme.rst模块
对我来说,到目前为止最容易使用和最好看的是命令
例如:需要用
通常使用
用
这一进展直接归功于
1 | 7.46GB 0:33:40 [3.78MB/s] [ <=> ] |
完成后总结出来
1 2 3 | 15654912+0 records in 15654912+0 records out 8015314944 bytes (8.0 GB) copied, 2020.49 s, 4.0 MB/s |
这只适用于使用gnome zenity。Zenity为bash脚本提供了强大的本地接口:https://help.gnome.org/users/zenity/stable/
从Zenity进度条示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #!/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 |
我在shell脚本中为char repeating创建了一个重复字符字符串,得到了一个答案。对于需要显示进度条的脚本,我有两个相对较小的bash版本(例如,一个循环遍历许多文件,但对于大tar文件或复制操作不有用)。更快的一个由两个函数组成,一个函数用于为条形图显示准备字符串:
1 2 3 4 5 6 7 | preparebar() { # $1 - bar length # $2 - bar char barlen=$1 barspaces=$(printf"%*s""$1") barchars=$(printf"%*s""$1" | tr ' '"$2") } |
一个显示进度条:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | progressbar() { # $1 - number (-1 for clearing the bar) # $2 - max number if [ $1 -eq -1 ]; then printf" $barspaces " else barch=$(($1*barlen/$2)) barsp=$((barlen-barch)) printf" [%.${barch}s%.${barsp}s] ""$barchars""$barspaces" fi } |
它可以用作:
1 | preparebar 50"#" |
这意味着准备带有50个""字符的条形图字符串,然后:
1 | progressbar 35 80 |
将显示对应于35/80比率的""字符数:
1 | [##################### ] |
请注意,在您(或其他程序)打印换行符之前,函数会反复在同一行上显示该条。如果将-1作为第一个参数,则该条将被删除:
1 | progressbar -1 80 |
较慢的版本具有一个功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | progressbar() { # $1 - number # $2 - max number # $3 - number of '#' characters if [ $1 -eq -1 ]; then printf" %*s ""$3" else i=$(($1*$3/$2)) j=$(($3-i)) printf" [%*s""$i" | tr ' ' '#' printf"%*s] ""$j" fi } |
它可以用作(与上面的示例相同):
1 | progressbar 35 80 50 |
如果在stderr上需要progressbar,只需在每个printf命令的末尾添加
apt样式进度条(不中断正常输出)
编辑:要获得更新版本,请检查我的GitHub页面
我对这个问题的回答不满意。我个人想要的是一个奇特的进度条,正如APT所看到的。
我查看了apt的C源代码,并决定为bash编写自己的等效代码。
这个进度条将很好地保持在终端的底部,不会干扰发送到终端的任何输出。
请注意,该条目前固定为100个字符宽。如果您希望将其扩展到终端的大小,这也相当容易完成(我的Github页面上的更新版本处理得很好)。
我会把我的剧本贴在这里。使用实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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上的版本):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | #!/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" " # 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" " } 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}" } |
这是它的样子
上传文件
1 | [##################################################] 100% (137921 / 137921 bytes) |
等待作业完成
1 | [######################### ] 50% (15 / 30 seconds) |
实现它的简单函数
你可以复制粘贴到你的脚本。它不需要任何其他东西来工作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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) " } |
使用实例
在这里,我们上传一个文件并在每次迭代时重新绘制进度条。只要我们能得到两个值:最大值和当前值,实际执行的作业就不重要。
在下面的示例中,最大值是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # 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" " |
要指示活动进度,请尝试以下命令:
1 2 3 4 5 | while true; do sleep 0.25 && echo -ne" \" && sleep 0.25 && echo -ne" |" && sleep 0.25 && echo -ne" /" && sleep 0.25 && echo -ne" -"; done; |
或
1 2 3 4 5 | while true; do sleep 0.25 && echo -ne" Activity: \" && sleep 0.25 && echo -ne" Activity: |" && sleep 0.25 && echo -ne" Activity: /" && sleep 0.25 && echo -ne" Activity: -"; done; |
或
1 2 3 4 5 6 | while true; do sleep 0.25 && echo -ne" " && sleep 0.25 && echo -ne" >" && sleep 0.25 && echo -ne" >>" && sleep 0.25 && echo -ne" >>>"; sleep 0.25 && echo -ne" >>>>"; done; |
或
1 2 3 4 5 6 7 | while true; do sleep .25 && echo -ne" :Active:" && sleep .25 && echo -ne" :aCtive:" && sleep .25 && echo -ne" :acTive:" && sleep .25 && echo -ne" :actIve:" && sleep .25 && echo -ne" :actiVe:" && sleep .25 && echo -ne" :activE:"; done; |
可以在while循环中使用标志/变量来检查和显示进度的值/范围。
利用上面列出的建议,我决定实现自己的进度条。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | #!/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=" " 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" Progress: [%s%s] %s %s${new_line}" \ "$done_bar" \ "$none_bar" \ "${spinner:x++%${#spinner}:1}" \ "$progress_message" } main"$@" |
基于EdouardLopez的工作,我创建了一个适合屏幕大小的进度条,不管它是什么。过来看。
它也发布在Git Hub上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | #!/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" " done printf" " exit 0 |
享受
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #!/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; |
输出:
1 2 3 4 5 6 7 8 9 | 0 % 12 % 25 % 37 % 50 % 62 % 75 % 87 % 100 % |
注意:如果输入1而不是255,则在输入2标准输出的情况下监视标准(但必须修改源以将"tot"设置为预计的输出文件大小)。
我为一个嵌入式系统做了一个纯粹的shell版本,利用:
/usr/bin/dd的sigusr1信号处理功能。
基本上,如果发送一个"kill sigusr1$(pid_running_dd_process)",它将输出吞吐量速度和传输量的摘要。
回溯DD,然后定期查询更新,并生成哈希滴答声就像以前的FTP客户端那样。
使用/dev/stdout作为非stdout友好程序(如scp)的目标
最终的结果允许您执行任何文件传输操作,并获取类似于老式ftp"hash"输出的进度更新,在这里您只需为每个x字节获取一个hash标记。
这不是生产质量代码,但你知道。我觉得很可爱。
就其价值而言,实际字节数可能无法正确反映在哈希数中——根据舍入问题的不同,您可能或多或少有一个。不要把它作为测试脚本的一部分,它只是一个眼糖。而且,是的,我知道这是非常低效的——这是一个shell脚本,我不为此道歉。
最后提供了wget、scp和tftp示例。它应该可以处理任何已经发出数据的东西。确保对不支持stdout的程序使用/dev/stdout。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | #!/bin/sh # # Copyright (C) Nathan Ramella ([email protected]) 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" DURATION=%d BYTES=%d KBPS=%f MD5=%s " \ $DURATION \ $SIZE \ $KBPS \ $MD5 } |
实例:
1 2 3 4 5 6 7 8 | 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 |
我建立在Fearside提供的答案之上
这将连接到Oracle数据库以检索RMAN还原的进度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | #!/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" Progress : [${_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 ' Finished! ' |
https://github.com/extensionsapp/progre.sh网站
创造40%的进步:
如果您需要显示一个时间进度条(通过提前知道显示时间),您可以使用python,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #!/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(' ') 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脚本保存为
1 | python progressbar.py 10 50 |
它将显示一个进度条大小的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | #!/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}%) " sleep 1 if [ $input -eq 10 ] ;then echo -ne ' ' fi } |
可以创建一个函数,以1-10的比例绘制条形图:
1 2 3 4 5 6 7 8 9 | 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 |
创建tar进度条
1 | tar xzvf pippo.tgz |xargs -L 19 |xargs -I@ echo -n"." |
其中"19"是tar中的文件数除以预期进度条的长度。示例:.tgz包含140个文件,您需要一个76"."的进度条,您可以放置-l 2。
你不需要别的了。
我想根据命令输出的行数和上一次运行的目标行数来跟踪进度:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | #!/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 '] ' } function clearbar { local size=$1 printf" %${size}s " echo -ne" " } 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 $? } |
然后这样使用:
1 2 3 | target=$(lines build.log 1000) (mvn clean install > build.log 2>&1) & progress $! $target build.log |
它输出的进度条如下所示:
1 | [===============================================> ] |
当输出的行数达到目标值时,条形图就会增大。如果行数超过了目标值,那么条线就会重新开始(希望目标值是好的)。
顺便说一句:我在mac osx上使用bash。我根据Mariascio的旋转器编写的代码。
首先将进程执行到后台,然后经常观察它的运行状态,即正在运行的打印模式,然后再次检查它的状态是否正在运行;
使用while循环经常监视进程的状态。
使用pgrep或任何其他命令来监视和获取进程的运行状态。
如果使用pgrep,则根据需要将不必要的输出重定向到/dev/null。
代码:
1 2 | sleep 12& while pgrep sleep &> /dev/null;do echo -en"#";sleep 0.5;done |
此""将一直打印到睡眠终止,此方法用于实现程序的进度条。
您还可以将此方法用于命令shell脚本,以便以可视方式分析IT处理时间。
缺陷:此pgrep方法不适用于所有情况,意外的是另一个进程正在以相同的名称运行,while循环没有结束。
因此,通过指定进程的PID来获取进程的运行状态,使用希望这个过程能与一些命令一起使用,
命令ps a将列出ID为的所有进程,您需要grep来找出指定进程的pid。
这是nexace为bash脚本编写的迷幻进度条。它可以从命令行调用为"./progressbar x y",其中"x"是以秒为单位的时间,"y"是与该进度部分关联的消息。
如果您希望脚本的其他部分控制ProgressBar,那么内部progressBar()函数本身独立性也很好。例如,发送"ProgressBar 10"创建目录树";"将显示:
1 | [####### ] (10%) Creating directory tree |
当然,这会很好地迷幻…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | #!/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 " }; 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" " }; timeprogress"$1""$2" |
有一次我也有一个繁忙的脚本,占用了好几个小时却没有显示任何进展。因此,我实现了一个功能,主要包括前面答案的技术:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | #!/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"% " 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 |
我今天也有同样的事情要做,基于diomidis的答案,我是这样做的(linux debian 6.0.7)。也许,这可以帮助你:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #!/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 % " done |