在bash或*NIX中的任何其他shell中编写脚本时,在运行将需要几秒钟的命令时,需要一个进度条.
例如,复制一个大文件,打开一个大的tar文件.
您建议以哪些方式向shell脚本添加进度条?
您可以通过覆盖一行来实现此目的.用于\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提到这个"失败":在这种情况下,你需要覆盖长行的长度(例如,用空格).
您可能还对如何进行微调器感兴趣:
当然!
i=1 sp="/-\|" echo -n ' ' while true do printf "\b${sp:i++%${#sp}:1}" done每次循环迭代时,它都会显示sp字符串中的下一个字符,并在到达结尾时回绕.(i是要显示的当前字符的位置,$ {#sp}是sp字符串的长度).
\ b字符串由'backspace'字符替换.或者,您可以使用\ r来回到行的开头.
如果你想让它变慢,可以在循环中放入一个sleep命令(在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
一些帖子展示了如何显示命令的进度.为了计算它,你需要看看你进步了多少.在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脚本出现在我的博客上.
使用linux命令pv:
http://linux.die.net/man/1/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-$_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
GNU tar有一个有用的选项,它提供了一个简单进度条的功能.
(...)另一个可用的检查点动作是'dot'(或'.').它指示tar在标准列表流上打印单个点,例如:
$ 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
我还想贡献自己的进度条
它通过使用Half unicode块实现了子字符精度
代码包括在内
没有看到类似的东西......我非常简单的解决方案:
#!/bin/sh BAR='####################' # this is full bar, e.g. 20 chars for i in {1..20}; do echo -ne "\r${BAR:0:$i}" # print $i chars of $BAR from 0 position sleep .1 # wait 100ms between "frames" done
{1..20}
- 最后没有新行打印
echo -n
- 打印时解释特殊字符
echo -e
- 回车,返回行首的特殊字符
我很久以前在一个简单的"黑客视频"中使用它来模拟打字代码.;)
这使您可以看到命令仍在执行:
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
命令或任何所需的命令.当该命令完成执行时,然后终止在后台运行的最后一个作业 - 这是无限循环.
上载档案
[##################################################] 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"
编辑:有关更新的版本,请检查我的github页面
我对这个问题的答复不满意。我个人正在寻找的是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}" }