当在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提到,如果您以长行开始,然后想要编写短行,那么这种方法“失败”:在这种情况下,您将需要覆盖长行的长度(例如,使用空格)。

其他回答

前几天我写了一个简单的进度条函数:

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

我今天有同样的事情要做,根据Diomidis的答案,下面是我是如何做到的(linux debian 6.0.7)。 也许,这可以帮助你:

#!/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 %\r"
done

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

预览

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

这可以通过一种相当简单的方式来实现:

使用for循环从0迭代到100 每一步睡眠25毫秒(0.25秒) 在$bar变量后面附加另一个=号,使进度条变宽 返回进度条和百分比(\r清除行并返回行首;-ne使echo在结尾不添加换行符,并解析\r特殊字符)

function progress {
    bar=''
    for (( x=0; x <= 100; x++ )); do
        sleep 0.25
        bar="${bar}="
        echo -ne "$bar ${x}%\r"
    done
    echo -e "\n"
}
$ progress
> ========== 10% # here: after 2.5 seconds
$ progress
> ============================== 30% # here: after 7.5 seconds

彩色进度条

function progress {
    bar=''
    for (( x=0; x <= 100; x++ )); do
        sleep 0.05
        bar="${bar} "

        echo -ne "\r"
        echo -ne "\e[43m$bar\e[0m"

        local left="$(( 100 - $x ))"
        printf " %${left}s"
        echo -n "${x}%"
    done
    echo -e "\n"
}

要使进度条变得彩色,你可以使用格式化转义序列-这里进度条是黄色的:\e[43m,然后我们用\e[0m重置自定义设置,否则即使进度条完成了,它也会影响进一步的输入。

对我来说,到目前为止最容易使用和最好看的是命令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