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

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

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


当前回答

我更喜欢使用dialog和——gauge参数。在许多发行版的.deb包安装和其他基本配置中经常使用。所以你不需要重新发明轮子……再一次

只要输入一个从1到100的int值@stdin。举个简单而愚蠢的例子:

for a in {1..100}; do sleep .1s; echo $a| dialog --gauge "waiting" 7 30; done

我有这个/bin/Wait文件(带有chmod u+x perms)用于烹饪

#!/bin/bash
INIT=`/bin/date +%s`
NOW=$INIT
FUTURE=`/bin/date -d "$1" +%s`
[ $FUTURE -a $FUTURE -eq $FUTURE ] || exit
DIFF=`echo "$FUTURE - $INIT"|bc -l`

while [ $INIT -le $FUTURE -a $NOW -lt $FUTURE ]; do
    NOW=`/bin/date +%s`
    STEP=`echo "$NOW - $INIT"|bc -l`
    SLEFT=`echo "$FUTURE - $NOW"|bc -l`
    MLEFT=`echo "scale=2;$SLEFT/60"|bc -l`
    TEXT="$SLEFT seconds left ($MLEFT minutes)";
    TITLE="Waiting $1: $2"
    sleep 1s
    PTG=`echo "scale=0;$STEP * 100 / $DIFF"|bc -l`
    echo $PTG| dialog --title "$TITLE" --gauge "$TEXT" 7 72
done

if [ "$2" == "" ]; then msg="Espera terminada: $1";audio="Listo";
else msg=$2;audio=$2;fi 

/usr/bin/notify-send --icon=stock_appointment-reminder-excl "$msg"
espeak -v spanish "$audio"

所以我可以写:

等"34分钟" "预热烤箱"

or

等待“12月31日”“新年快乐”

其他回答

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

预览

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

这是它看起来的样子

上传文件

[##################################################] 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
function lines {
  local file=$1
  local default=$2
  if [[ -f $file ]]; then
    wc -l $file | awk '{print $1}';
  else
    echo $default
  fi
}

function bar {
  local items=$1
  local total=$2
  local size=$3
  percent=$(($items*$size/$total % $size))
  left=$(($size-$percent))
  chars=$(local s=$(printf "%${percent}s"); echo "${s// /=}")
  echo -ne "[$chars>";
  printf "%${left}s"
  echo -ne ']\r'
}

function clearbar {
  local size=$1
  printf " %${size}s  "
  echo -ne "\r"
}

function progress {
  local pid=$1
  local total=$2
  local file=$3

  bar 0 100 50
  while [[ "$(ps a | awk '{print $1}' | grep $pid)" ]]; do
    bar $(lines $file 0) $total 50
    sleep 1
  done
  clearbar 50
  wait $pid
  return $?
}

然后这样使用它:

target=$(lines build.log 1000)
(mvn clean install > build.log 2>&1) &
progress $! $target build.log

它输出一个进度条,看起来像这样:

[===============================================>   ]

条形图随着输出行数达到目标而增长。如果行数超过了目标,条就重新开始(希望目标是好的)。

BTW:我在Mac OSX上使用bash。我基于这个代码从mariascio旋转。

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