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

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

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


当前回答

要制作一个tar进度条

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

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

你什么都不需要了。

其他回答

许多答案描述了编写自己的命令来打印'\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

这是它看起来的样子

上传文件

[##################################################] 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"

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

使用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重置自定义设置,否则即使进度条完成了,它也会影响进一步的输入。

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

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

这可以让你看到命令仍在执行:

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

这将创建一个无限while循环,在后台执行,并每秒回显一个"."。这将显示出来。在壳里。运行tar命令或任何您需要的命令。当该命令执行完毕时,终止在后台运行的最后一个作业——即无限while循环。