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

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

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


当前回答

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

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

其他回答

大多数unix命令不会为您提供可以执行此操作的直接反馈。 有些将在您可以使用的stdout或stderr上提供输出。

对于tar之类的东西,您可以使用-v开关并将输出管道到一个程序,该程序为读取的每一行更新一个小动画。当tar写出一个文件列表时,程序可以更新动画。要计算完成百分比,您必须知道文件的数量并计算行数。

据我所知,cp没有给出这种输出。要监视cp的进程,必须监视源文件和目标文件,并监视目标文件的大小。你可以写一个小的c程序,使用stat(2)系统调用来获取文件大小。这将读取源文件的大小,然后轮询目标文件,并根据到目前为止写入的文件的大小更新% complete条。

要指示活动的进度,请尝试以下命令:

while true; do sleep 0.25 && echo -ne "\r\\" && sleep 0.25 && echo -ne "\r|" && sleep 0.25 && echo -ne "\r/" && sleep 0.25 && echo -ne "\r-"; done;

OR

while true; do sleep 0.25 && echo -ne "\rActivity: \\" && sleep 0.25 && echo -ne "\rActivity: |" && sleep 0.25 && echo -ne "\rActivity: /" && sleep 0.25 && echo -ne "\rActivity: -"; done;

OR

while true; do sleep 0.25 && echo -ne "\r" && sleep 0.25 && echo -ne "\r>" && sleep 0.25 && echo -ne "\r>>" && sleep 0.25 && echo -ne "\r>>>"; sleep 0.25 && echo -ne "\r>>>>"; done;

OR

while true; do sleep .25 && echo -ne "\r:Active:" && sleep .25 && echo -ne "\r:aCtive:" && sleep .25 && echo -ne "\r:acTive:" && sleep .25 && echo -ne "\r:actIve:" && sleep .25 && echo -ne "\r:actiVe:" && sleep .25 && echo -ne "\r:activE:"; done;

可以在while循环中使用标志/变量来检查和显示进度的值/范围。

这是它看起来的样子

上传文件

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

你可能也对如何做一个旋转器感兴趣:

我能在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

My solution displays the percentage of the tarball that is currently being uncompressed and written. I use this when writing out 2GB root filesystem images. You really need a progress bar for these things. What I do is use gzip --list to get the total uncompressed size of the tarball. From that I calculate the blocking-factor needed to divide the file into 100 parts. Finally, I print a checkpoint message for each block. For a 2GB file this gives about 10MB a block. If that is too big then you can divide the BLOCKING_FACTOR by 10 or 100, but then it's harder to print pretty output in terms of a percentage.

假设您正在使用Bash,那么您可以使用 shell函数

untar_progress () 
{ 
  TARBALL=$1
  BLOCKING_FACTOR=$(gzip --list ${TARBALL} |
    perl -MPOSIX -ane '$.==2 && print ceil $F[1]/50688')
  tar --blocking-factor=${BLOCKING_FACTOR} --checkpoint=1 \
    --checkpoint-action='ttyout=Wrote %u%  \r' -zxf ${TARBALL}
}