如何将进度条添加到shell脚本?

当在* NIX中的bash或任何其他shell中编写脚本时,如果运行一个需要几秒钟的命令,则需要一个进度条。

例如,复制一个大文件,打开一个大的tar文件。

你build议如何将进度条添加到shell脚本中?

你可以通过覆盖一行来实现。 使用\r返回到行的开头,而不会将\n写入terminal。

写完\n当你完成了线路。

使用echo -ne来:

  1. 不打印\n
  2. 识别\r等转义序列。

这是一个演示:

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

有些post展示了如何显示命令的进度。 为了计算它,你需要看看你有多less进展。 在BSD系统上,一些命令(如dd(1))接受一个SIGINFO信号,并报告它们的进度。 在Linux系统上,一些命令会对SIGUSR1作出类似的响应。 如果此工具可用,则可以通过ddpipe道input来监视处理的字节数。

或者,您可以使用lsof来获取文件读取指针的偏移量,从而计算进度。 我写了一个名为pmonitor的命令,显示处理指定进程或文件的进度。 有了它,你可以做的事情,如下所示。

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

早期版本的Linux和FreeBSD shell脚本出现在我的博客上 。

你也可能对如何做一个微调器感兴趣:

我可以在Bash做一个微调吗?

当然!

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

每次循环迭代时,它都会显示spstring中的下一个字符,并在到达结尾时将其包围。 (i是要显示的当前字符的位置,$ {#sp}是spstring的长度)。

\ bstring被replace为“退格”字符。 或者,您可以使用\ r回到行的开头。

如果你想减慢速度,在循环内部放一个睡眠命令(在printf之后)。

一个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 

使用linux命令pv:

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

它不知道大小,如果它在中间的stream,但它提供了一个速度和总,从那里你可以找出需要多长时间,并获得反馈,所以你知道它没有挂起。

有一个简单的进度条function,我写了一天:

 #!/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/

GNU tar有一个有用的选项,它提供了一个简单进度条的function。

(…)另一个可用的检查点操作是“点”(或“。”)。 它指示tar在标准列表stream上打印一个点,例如:

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

同样的效果可以通过以下方式获得:

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

使用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 

这使您可以看到一个命令仍在执行:

 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 

这将创build一个无限的while循环 ,在后台执行并回显 “。” 每一秒。 这将显示. 在壳里。 运行tar命令或任何你想要的命令。 当该命令完成执行后, 杀死在后台运行的最后一个作业 – 这是无限的while循环

我正在寻找比选定的答案更性感的东西,我自己的脚本也是如此。

预习

progress-bar.sh在行动

资源

我把它放在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 100 

首先酒吧不是唯一一个pipe道进度计。 另一个(也许更为人所知)是pv(pipe道查看器)。

其次酒吧和光伏可以用来例如这样的:

 $ 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,则使用进程replace :

 $ copy <(bar file1) file2 $ copy <(pv file1) file2 

进程replace是一种bash魔术,它创build了临时的fifopipe道文件/ dev / fd /,并通过这个pipe道将runout进程(内部括号)的stdout连接起来,副本就像普通文件一样(除了一个例外,它只能读取它前锋)。

更新:

酒吧命令本身也允许复制。 男子酒吧后:

 bar --in-file /dev/rmt/1cbn --out-file \ tape-restore.tar --size 2.4g --buffer-size 64k 

但是在我看来,stream程replace是更普遍的做法。 它使用cp程序本身。

没有看到类似的东西,所以…我非常简单的解决scheme:

 #!/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" – 回车,一个特殊的字符返回到行首

我很久以前用一个简单的“黑客video”来模拟input代码。 ;)

大多数Unix命令不会给你这种直接的反馈,你可以做到这一点。 有些会给你输出你可以使用的标准输出或标准错误。

对于像tar这样的东西,你可以使用-v开关,并将输出pipe道输出到一个程序,它为它读取的每一行更新一个小的animation。 当tar写出一个文件列表时,程序可以更新animation。 要完成一个百分比,你将不得不知道文件的数量和计数行数。

就我所知,cp并没有给出这种输出。 要监视cp的进度,您必须监视源文件和目标文件,并观察目标的大小。 您可以使用stat(2)系统调用来编写一个小型的c程序来获取文件大小。 这将读取源文件的大小,然后轮询目标文件,并基于最近写入的文件的大小更新%完成栏。

我的解决scheme显示当前正在解压缩和写入的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} } 

我宁愿使用–gauge param 对话框 。 在.deb包安装和许多发行版的其他基本configuration的东西中经常使用。 所以你不需要重新发明轮子

只要把一个int值从1到100 @stdin。 一个基本和愚蠢的例子:

 for a in {1..100}; do sleep .1s; echo $a| dialog --gauge "waiting" 7 30; done 

我有这个/ bin /等待文件(用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"

对我来说,最简单的方法就是使用最好看的东西,就像一些已经写好的人一样

例如:需要使用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 

要指示活动的进度,请尝试以下命令:

 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循环内的标志/variables来检查和显示进度的值/范围。

我也想贡献我自己的进度条

它通过使用Half unicode块来实现子字符精度

在这里输入图像描述

代码包括在内

我用了一个在shell脚本中创build重复字符的string来做字符重复的答案。 我有两个相对较小的bash版本,用于需要显示进度条的脚本(例如,循环经过许多文件,但对大型tar文件或复制操作不起作用)。 较快的一个由两个function组成,一个为棒状显示准备string:

 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个“#”字符的string,然后:

 progressbar 35 80 

将显示对应于35/80比率的“#”字符的数量:

 [##################### ] 

请注意,函数一遍又一遍地在同一行显示栏,直到您(或某个其他程序)打印换行符为止。 如果您将-1作为第一个参数,则会删除该条:

 progressbar -1 80 

较慢的版本都在一个function:

 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上的进度条,只需在每个printf命令的末尾添加>&2

许多答案描述了编写自己的命令来打印出'\r' + $some_sort_of_progress_msg 。 有时问题是,每秒打印出数百个这样的更新将会减慢这个过程。

然而,如果你的任何进程产生输出(例如7z a -r newZipFile myFolder会在压缩时输出每个文件名),那么就存在一个更简单,快速,无痛且可自定义的解决scheme。

安装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代码中包装迭代。

https://github.com/tqdm/tqdm/blob/master/README.rst#module

基于Edouard Lopez的工作,我创build了一个适合屏幕大小的进度条,不pipe它是什么。 一探究竟。

在这里输入图像描述

它也张贴在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 

请享用

这只适用于使用gnome 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 

我为embedded式系统做了一个纯shell版本,

  • / usr / bin / dd的SIGUSR1信号处理function。

    基本上,如果您发送'kill SIGUSR1 $(pid_of_running_dd_process)',它将输出吞吐速度和传输量的汇总。

  • 后台dd,然后定期查询更新,并生成散列嘀嗒如旧的ftp客户端用来。

  • 使用/ dev / stdout作为非标准输出友好程序(如scp)的目标

最终的结果可以让你进行任何文件传输操作,并得到进度更新,看起来像老派的FTP“散列”输出,你只要得到一个散列标记为每个X字节。

这几乎不是生产质量代码,但你明白了。 我觉得很可爱

对于它的价值,实际的字节数可能无法正确反映在散列的数量 – 你可能有一个或多或less取决于舍入问题。 不要将它用作testing脚本的一部分,它只是眼睛的一部分。 而且,是的,我知道这是非常低效的 – 这是一个shell脚本,我不会道歉。

最后提供wget,scp和tftp的示例。 它应该处理任何发送数据的东西。 确保使用/ 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 

我已经build立在恐惧提供的答案上

这将连接到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' 

这是nExace针对bash脚本的迷幻进度条。 它可以从命令行调用为“./progressbar xy”,其中“x”是以秒为单位的时间,“y”是与该部分进程相关的消息。

inner progressbar()函数本身也是很好的独立的,如果你想要脚本的其他部分来控制进度条。 例如,发送“进度条10”创build目录树“;” 将显示:

 [####### ] (10%) Creating directory tree 

当然这将是很好的迷幻虽然…

 #!/bin/bash if [ "$#" -eq 0 ]; then echo "x is \"time in seconds\" and z is \"message\""; echo "Usage: progressbar xz"; 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" 

首先将程序执行到后台,然后频繁地观察它的运行状态,即正在运行打印模式,并再次检查其状态是否正在运行;

使用while循环来频繁观察进程的状态。

使用pgrep或任何其他命令来观察和获取进程的运行状态。

如果使用pgrep根据需要将不必要的输出redirect到/ dev / null。

码:

 sleep 12& while pgrep sleep &> /dev/null;do echo -en "#";sleep 0.5;done 

这个“#”会打印直到睡眠终止,这个方法用来实现程序进度时间的进度条。

你也可以使用这个方法来把shell脚本的命令作为可视化的分析处理时间。

BUG:这个pgrep方法在所有情况下都不起作用,意外的是另一个进程是用相同的名字运行的,while循环没有结束。

所以通过指定它的PID来获得进程运行状态,使用可能的进程可以用一些命令,

命令ps a会列出所有进程的id,你需要grep来找出指定进程的pid

我想根据一个命令输出的行数与前一次运行的目标行数进行跟踪:

 #!/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 

它输出一个进度条,看起来像这样:

 [===============================================> ] 

随着输出的行数达到目标,条形增长。 如果行数超过了目标,则重新开始(希望目标是好的)。

顺便说一句:我在Mac OSX上使用bash。 我基于mariascio的一个微调这个代码。

 #!/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 } 

可以创build一个函数,在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 

如果你必须显示时间进度条(通过预先知道显示时间),你可以使用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秒钟。

制作一个tar进度条

 tar xzvf pippo.tgz |xargs -L 19 |xargs -I@ echo -n "." 

其中“19”是焦油中的文件数目除以预期的进度条的长度。 例如:.tgz包含140个文件,你需要一个76“。”的进度条,你可以把-L 2。

你什么都不需要

一次,我也有一个忙碌的脚本,被占用了几个小时,没有任何进展。 所以我实现了一个主要包含以前答案的技巧的函数:

 #!/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