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

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

你建议用什么方法向shell脚本添加进度条?


当前回答

在我的系统上使用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

其他回答

首先,杆并不是唯一的管道进度仪表。另一个(可能更广为人知)是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程序。

如果你必须显示一个临时进度条(通过提前知道显示时间),你可以像下面这样使用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秒。

在我的系统上使用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

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

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

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

同样的效果可以通过:

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

这只适用于使用地精天顶。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