当在bash或*NIX中的任何其他shell中编写脚本时,在运行需要超过几秒钟时间的命令时,需要一个进度条。
例如,复制一个大文件,打开一个大tar文件。
你建议用什么方法向shell脚本添加进度条?
当在bash或*NIX中的任何其他shell中编写脚本时,在运行需要超过几秒钟时间的命令时,需要一个进度条。
例如,复制一个大文件,打开一个大tar文件。
你建议用什么方法向shell脚本添加进度条?
当前回答
许多答案描述了编写自己的命令来打印'\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代码中包装可迭代对象。
https://github.com/tqdm/tqdm/blob/master/README.rst#module
其他回答
对我来说,到目前为止最容易使用和最好看的是命令pv或bar,就像某人已经写的那样
例如:需要用dd备份整个驱动器
通常你使用dd if="$input_drive_path" of="$output_file_path"
对于pv,你可以这样做:
如果dd = input_drive_path美元“硒| | dd =“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
首先,杆并不是唯一的管道进度仪表。另一个(可能更广为人知)是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来处理参数中给出的文件,比如copy file1 file2,一个有用的技巧是使用进程替换:
$ 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程序。
#!/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的比例绘制bar数:
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
一些帖子已经展示了如何显示命令的进度。为了计算它,你需要看看你已经进步了多少。在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脚本的早期版本出现在我的博客上(“在Unix上监视进程进度”)。
你可能也对如何做一个旋转器感兴趣:
我能在Bash中做旋转吗?
Sure! i=1 sp="/-\|" echo -n ' ' while true do printf "\b${sp:i++%${#sp}:1}" done Each 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 \r 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: sp='/-\|' printf ' ' while true; do printf '\b%.1s' "$sp" sp=${sp#?}${sp%???} done If 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: 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