Bash 如何向shell脚本添加进度条?

Bash 如何向shell脚本添加进度条?,bash,shell,zsh,Bash,Shell,Zsh,在bash或*NIX中的任何其他shell中编写脚本时,在运行一个需要几秒钟以上时间的命令时,需要一个进度条 例如,复制一个大文件,打开一个大tar文件 您建议用什么方法将进度条添加到shell脚本?大多数unix命令不会提供您可以使用的那种直接反馈。 有些会在stdout或stderr上提供您可以使用的输出 对于类似tar的东西,您可以使用-v开关并将输出传输到一个程序,该程序为它读取的每一行更新一个小动画。当tar写出一个文件列表时,程序可以更新动画。要计算完成百分比,您必须知道文件的数量并

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

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


您建议用什么方法将进度条添加到shell脚本?

大多数unix命令不会提供您可以使用的那种直接反馈。 有些会在stdout或stderr上提供您可以使用的输出

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


据我所知,cp没有给出这种输出。要监视cp的进度,您必须监视源文件和目标文件,并监视目标文件的大小。您可以使用系统调用编写一个小型c程序来获取文件大小。这将读取源文件的大小,然后轮询目标文件,并根据到目前为止写入的文件大小更新完成百分比栏。

您可以通过覆盖一行来实现这一点。使用\r返回到行的开头,而不写入终端

#!/bin/bash

# test_progress_bar3

Main () {

    tput civis                              # Turn off cursor
    for ((i=0; i<=100; i++)); do
        CurrLevel=$(./progress_bar3 "$i")   # Generate progress bar 0 to 100
        echo -ne "$CurrLevel"\\r            # Reprint overtop same line
        sleep .04
    done
    echo -e \\n                             # Advance line to keep last progress
    echo "$0 Done"
    tput cnorm                              # Turn cursor back on
} # Main

Main "$@"
当您完成推进该行时写入\n

使用echo-ne执行以下操作:

不打印\n和 要识别转义序列,如\r\n。 下面是一个演示:

echo -ne '#####                     (33%)\r'
sleep 1
echo -ne '#############             (66%)\r'
sleep 1
echo -ne '#######################   (100%)\r'
echo -ne '\n'

在下面的一条评论中,puk提到,如果你从一个长行开始,然后想写一个短行,那么这将失败:在这种情况下,你需要覆盖长行的长度,例如,使用空格。

一些帖子展示了如何显示命令的进度。为了计算它,你需要看看你进步了多少。在BSD系统上,某些命令(如dd1)接受SIGINFO信号,并将报告其进度。在Linux系统上,一些命令将类似地响应SIGUSR1。如果此功能可用,您可以通过dd通过管道输入以监视处理的字节数

或者,您可以使用获取文件读取指针的偏移量,从而计算进度。我编写了一个名为的命令,用于显示处理指定进程或文件的进度。有了它,你可以做以下事情

$pmonitor-c gzip /home/dds/data/mysql-2015-04-01.sql.gz 58.06%
较早版本的Linux和FreeBSD 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

您可能还对以下方面感兴趣:

我可以在Bash中做旋转器吗? 当然

i=1
sp="/-\|"
echo -n ' '
while true
do
    printf "\b${sp:i++%${#sp}:1}"
done
每次循环迭代时,它都会显示sp中的下一个字符 绳子,到达末端时缠绕。我的职位是 要显示的当前字符和${sp}是sp的长度 绳子

\b字符串被“退格”字符替换。或者, 您可以使用\r返回到行的开头

如果你想让它慢下来,就在循环中放一个sleep命令 在printf之后

POSIX的等价物是:

sp='/-\|'
printf ' '
while true; do
    printf '\b%.1s' "$sp"
    sp=${sp#?}${sp%???}
done
如果您已经有了一个做大量工作的循环,那么可以调用 在每次迭代开始时执行以下函数以更新 微调器:

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
有一个有用的选项,它提供了一个简单进度条的功能

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

$ tar -c --checkpoint=1000 --checkpoint-action=dot /var
...
可通过以下方式获得相同的效果:

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

“我的解决方案”显示了 当前正在解压缩和写入。我用这个 写入2GB根文件系统映像时。你真的 这些东西需要一个进度条。我所做的就是使用 gzip-列表以获取文件的总未压缩大小 柏油球。由此我计算出所需的阻塞因子 将文件分成100个部分。最后,我打印了一个 每个块的检查点消息。对于2GB文件,请单击此处 一个块大约有10MB。如果那太大了,你可以 将阻塞因子除以10或100,则为 就百分比而言,很难打印漂亮的输出

假设您使用的是Bash,那么就可以使用 跟随壳函数

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}
}
使用Linux命令


如果它在流水线的中间,它不知道大小,但是它给出了速度和总数,从那里你可以计算出它应该花费多长时间并得到反馈,这样你就知道它没有被挂起。

< P>这使你可以看到命令仍然在执行:

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循环。

这是 仅适用于使用gnome zenity。Zenity为bash脚本提供了一个很棒的本机接口:

从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

首先,bar并不是唯一一个管道进度表。另一个可能更为人所知的是pv pipe viewer

其次,可以使用bar和pv,例如:

$ bar file1 | wc -l 
$ pv file1 | wc -l
[##----------------------------] 17128/218210 (7%)
甚至:

$ tail -n 100 file1 | bar | wc -l
$ tail -n 100 file1 | pv | wc -l
如果要在处理参数中给定文件的命令(如copy file1 file2)中使用bar和pv,一个有用的技巧是使用:


但在我看来,流程替换是更通用的方法。它使用cp程序本身。

我利用以下优势为嵌入式系统制作了纯shell版本:

/usr/bin/dd的SIGUSR1信号处理功能

基本上,如果您发送一个'kill SIGUSR1$pid_of_running_dd_process',它将输出 吞吐量速度和传输量的摘要

备份dd,然后定期查询更新,并生成 像旧式ftp客户端一样使用哈希标记

使用/dev/stdout作为非标准输出友好程序(如scp)的目标

最终结果允许您执行任何文件传输操作,并获得类似老式FTP“哈希”输出的进度更新,在这种输出中,您只需为每X字节获得一个哈希标记

这几乎不是生产质量代码,但你明白了。我觉得很可爱

值得一提的是,实际字节计数可能不会正确反映在散列数中——根据舍入问题,可能会有一个或多个或少个。不要把它作为测试脚本的一部分,它只是吸引眼球的东西。而且,是的,我知道这是非常低效的-这是一个shell脚本,我不为此道歉

最后提供了wget、scp和tftp的示例。它应该可以处理任何发出数据的东西。确保对不支持标准输出的程序使用/dev/stdout

#!/bin/sh
#
# Copyright (C) Nathan Ramella (nar+progress-script@remix.net) 2010 
# LGPLv2 license
# If you use this, send me an email to say thanks and let me know what your product
# is so I can tell all my friends I'm a big man on the internet!

progress_filter() {

        local START=$(date +"%s")
        local SIZE=1
        local DURATION=1
        local BLKSZ=51200
        local TMPFILE=/tmp/tmpfile
        local PROGRESS=/tmp/tftp.progress
        local BYTES_LAST_CYCLE=0
        local BYTES_THIS_CYCLE=0

        rm -f ${PROGRESS}

        dd bs=$BLKSZ of=${TMPFILE} 2>&1 \
                | grep --line-buffered -E '[[:digit:]]* bytes' \
                | awk '{ print $1 }' >> ${PROGRESS} &

        # Loop while the 'dd' exists. It would be 'more better' if we
        # actually looked for the specific child ID of the running 
        # process by identifying which child process it was. If someone
        # else is running dd, it will mess things up.

        # My PID handling is dumb, it assumes you only have one running dd on
        # the system, this should be fixed to just get the PID of the child
        # process from the shell.

        while [ $(pidof dd) -gt 1 ]; do

                # PROTIP: You can sleep partial seconds (at least on linux)
                sleep .5    

                # Force dd to update us on it's progress (which gets
                # redirected to $PROGRESS file.
                # 
                # dumb pid handling again
                pkill -USR1 dd

                local BYTES_THIS_CYCLE=$(tail -1 $PROGRESS)
                local XFER_BLKS=$(((BYTES_THIS_CYCLE-BYTES_LAST_CYCLE)/BLKSZ))

                # Don't print anything unless we've got 1 block or more.
                # This allows for stdin/stderr interactions to occur
                # without printing a hash erroneously.

                # Also makes it possible for you to background 'scp',
                # but still use the /dev/stdout trick _even_ if scp
                # (inevitably) asks for a password. 
                #
                # Fancy!

                if [ $XFER_BLKS -gt 0 ]; then
                        printf "#%0.s" $(seq 0 $XFER_BLKS)
                        BYTES_LAST_CYCLE=$BYTES_THIS_CYCLE
                fi
        done

        local SIZE=$(stat -c"%s" $TMPFILE)
        local NOW=$(date +"%s")

        if [ $NOW -eq 0 ]; then
                NOW=1
        fi

        local DURATION=$(($NOW-$START))
        local BYTES_PER_SECOND=$(( SIZE / DURATION ))
        local KBPS=$((SIZE/DURATION/1024))
        local MD5=$(md5sum $TMPFILE | awk '{ print $1 }')

        # This function prints out ugly stuff suitable for eval() 
        # rather than a pretty string. This makes it a bit more 
        # flexible if you have a custom format (or dare I say, locale?)

        printf "\nDURATION=%d\nBYTES=%d\nKBPS=%f\nMD5=%s\n" \
            $DURATION \
            $SIZE \
            $KBPS \
            $MD5
}
示例:

echo "wget"
wget -q -O /dev/stdout http://www.blah.com/somefile.zip | progress_filter

echo "tftp"
tftp -l /dev/stdout -g -r something/firmware.bin 192.168.1.1 | progress_filter

echo "scp"
scp user@192.168.1.1:~/myfile.tar /dev/stdout | progress_filter

对我来说,迄今为止最容易使用和最好看的是命令pv或bar,就像一些人已经写过的那样

例如:需要使用dd备份整个驱动器

通常使用dd if=$input\U drive\U path of=$output\U file\U path

有了pv,您可以这样做:

dd if=$input_drive_path | pv | dd of=$output_file_path

进展直接进入标准输出,如下所示:


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

#!/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'
或是从,

如果您必须通过提前知道显示时间来显示临时进度条,您可以使用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 ""

它将显示一个大小为50个字符并运行10秒的进度条。

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

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;

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

我使用了来自的答案来重复char。对于需要显示进度条的脚本,我有两个相对较小的bash版本,例如,一个遍历许多文件的循环,但对于大型tar文件或复制操作没有用处。更快的一个由两个函数组成,一个用于为条形图显示准备字符串:

preparebar() {
# $1 - bar length
# $2 - bar char
    barlen=$1
    barspaces=$(printf "%*s" "$1")
    barchars=$(printf "%*s" "$1" | tr ' ' "$2")
}
一个用于显示进度条:

progressbar() {
# $1 - number (-1 for clearing the bar)
# $2 - max number
    if [ $1 -eq -1 ]; then
        printf "\r  $barspaces\r"
    else
        barch=$(($1*barlen/$2))
        barsp=$((barlen-barch))
        printf "\r[%.${barch}s%.${barsp}s]\r" "$barchars" "$barspaces"
    fi
}
它可以用作:

preparebar 50 "#"
这意味着为包含50个字符的条准备字符串,然后:

progressbar 35 80
将显示与35/80比率对应的字符数:

[#####################                             ]
请注意,函数会在同一行上反复显示该条,直到您或其他程序打印换行符为止。如果将-1作为第一个参数,该条将被删除:

progressbar -1 80
较慢的版本是一个功能:

progressbar() {
# $1 - number
# $2 - max number
# $3 - number of '#' characters
    if [ $1 -eq -1 ]; then
        printf "\r  %*s\r" "$3"
    else
        i=$(($1*$3/$2))
        j=$(($3-i))
        printf "\r[%*s" "$i" | tr ' ' '#'
        printf "%*s]\r" "$j"
    fi
}
也可用作与上述相同的示例:

progressbar 35 80 50
如果您需要stderr上的progressbar,只需在每个printf命令末尾添加>&2即可

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

只要在stdin中输入一个从1到100的int值。一个基本而愚蠢的例子:

for a in {1..100}; do sleep .1s; echo $a| dialog --gauge "waiting" 7 30; done
我有一个带有chmod u+x perms的/bin/Wait文件,用于烹饪:p

所以我可以说:

等待34分钟,预热烤箱


等待12月31日新年快乐

许多答案描述了如何编写自己的命令来打印“\r”+$some\u sort\u\u progress\u msg。有时问题是每秒打印出数百个更新会减慢进程

但是,如果您的任何进程产生输出,例如7z a-r newZipFile myFolder将在压缩文件名时输出每个文件名,则存在一个更简单、快速、无痛且可定制的解决方案

安装python模块TQM

帮助:tqdm-h。使用更多选项的示例:

$ find / -name '*.py' -exec cat \{} \; | tqdm --unit loc --unit_scale True | wc -l
作为奖励,您还可以使用TQM在python代码中包装iterables


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

预览 来源 我戴上了


没有看到任何类似的东西,这里的所有自定义函数似乎都是 要专注于单独渲染,以便。。。下面是我非常简单的POSIX兼容解决方案,并提供了一步一步的解释,因为这个问题并不简单

TL;博士 渲染进度条非常容易。估计应该渲染多少是另一回事。这是如何渲染进度条动画-您可以将此示例复制并粘贴到文件并运行它:

#!/bin/sh

BAR='####################'   # this is full bar, e.g. 20 chars

for i in {1..20}; do
    echo -ne "\r${BAR:0:$i}" # print $i chars of $BAR from 0 position
    sleep .1                 # wait 100ms between "frames"
done
{1..20}-从1到20的值 echo-n-打印结束时不使用新行 echo-e-在打印时解释特殊字符 \r-回车符,返回到行开头的特殊字符 你可以让它以任何速度呈现任何内容,因此这种方法非常通用,例如,经常用于愚蠢电影中黑客行为的可视化,这不是开玩笑

完整答案 问题的核心是如何确定$i值,即显示多少进度条。在上面的例子中,我只是让它在for循环中递增来说明原理,但实际应用程序将使用一个无限循环,并在每次迭代中计算$I变量。要进行上述计算,需要以下要素:

还有多少工作要做 到目前为止已经做了多少工作 如果是cp,则需要源文件的大小和目标文件的大小:

#!/bin/sh

$src=/path/to/source/file
$tgt=/path/to/target/file

cp "$src" "$tgt" &                     # the & forks the `cp` process so the rest
                                       # of the code runs without waiting (async)

BAR='####################'

src_size=$(stat -c%s "$src")           # how much there is to do

while true; do
    tgt_size=$(stat -c%s "$tgt")       # how much has been done so far
    i=$(( $tgt_size * 20 / $src_size ))
    echo -ne "\r${BAR:0:$i}"
    if [ $tgt_size == $src_size ]; then
        echo ""                        # add a new line at the end
        break;                         # break the loop
    fi
    sleep .1
done
stat-检查文件统计信息 -c-返回格式化值 %s-总尺寸 对于文件解包之类的操作,计算源大小稍微困难一些,但仍然与获取未压缩文件的大小一样简单:

#!/bin/sh
src_size=$(gzip -l "$src" | tail -n1 | tr -s ' ' | cut -d' ' -f3)
gzip-l-显示有关zip存档的信息 尾部-n1-从底部开始使用一条线 tr-s“”-将多个空格转换为一个空格 剪切-d'-f3-剪切第三个空格分隔列 不过,问题的关键在于此。这个解决方案越来越不通用。所有实际进度的计算都与您试图可视化的域紧密相连,是单个文件操作、计时器倒计时、目录中文件数量的增加、对多个文件的操作等等,因此无法重用。唯一可重用的部分是进度条渲染。要重用它,您需要将其抽象并保存在文件中,例如/usr/lib/progress\u bar.sh,然后定义计算特定于域的输入值的函数。这是一个通用代码的样子,因为人们要求它,所以我也使$BAR成为动态的,其余的现在应该清楚了:

#!/bin/sh

BAR_length=50
BAR_character='#'
BAR=$(printf "%${BAR_length}s" | tr ' ' $BAR_character)

work_todo=$(get_work_todo)             # how much there is to do

while true; do
    work_done=$(get_work_done)         # how much has been done so far
    i=$(( $work_done * $BAR_length / $work_todo ))
    echo -ne "\r${BAR:0:$i}"
    if [ $work_done == $work_todo ]; then
        echo ""
        break;
    fi
    sleep .1
done
printf-以给定格式打印内容的内置程序 printf“%50s”-不打印,用50个空格填充 tr“”-将每个空格转换为哈希符号 下面是您使用它的方式:

#!/bin/sh

src=/path/to/source/file
tgt=/path/to/target/file

function get_work_todo() {
    echo $(stat -c%s "$src")
}

function get_work_done() {
    [ -e "$tgt" ] &&                   # if target file exists
        echo $(stat -c%s "$tgt") ||    # echo its size, else
        echo 0                         # echo zero
}

cp "$src" "$tgt" &                     # copy in the background

source /usr/lib/progress_bar.sh        # execute the progress bar

很明显,它可以被包装在一个函数中,被重写以使用管道流,被重写为其他语言,不管你的毒药是什么。

基于Edouard Lopez的工作,我创建了一个适合屏幕大小的进度条,不管它是什么。看看吧

它也发布在了


享受

我也想贡献我的

它通过使用


代码被包括在内

使用上面列出的建议,我决定实现我自己的进度条

#!/usr/bin/env bash

main() {
  for (( i = 0; i <= 100; i=$i + 1)); do
    progress_bar "$i"
    sleep 0.1;
  done
  progress_bar "done"
  exit 0
}

progress_bar() {
  if [ "$1" == "done" ]; then
    spinner="X"
    percent_done="100"
    progress_message="Done!"
    new_line="\n"
  else
    spinner='/-\|'
    percent_done="${1:-0}"
    progress_message="$percent_done %"
  fi

  percent_none="$(( 100 - $percent_done ))"
  [ "$percent_done" -gt 0 ] && local done_bar="$(printf '#%.0s' $(seq -s ' ' 1 $percent_done))"
  [ "$percent_none" -gt 0 ] && local none_bar="$(printf '~%.0s' $(seq -s ' ' 1 $percent_none))"

  # print the progress bar to the screen
  printf "\r Progress: [%s%s] %s %s${new_line}" \
    "$done_bar" \
    "$none_bar" \
    "${spinner:x++%${#spinner}:1}" \
    "$progress_message"
}

main "$@"
下面是它的外观 上传文件

等待作业完成

实现它的简单函数 您可以将其复制粘贴到脚本中。它不需要任何其他东西就能工作

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"
}
创建40%的进度:progreSh 40

APT样式进度条不会中断正常输出 编辑:有关更新版本,请检查我的

我对这个问题的回答不满意。我个人想要的是一个奇特的进度条,正如APT所看到的

我看了APT的C源代码,决定为bash编写自己的等效代码

此进度条将很好地位于终端底部,不会干扰发送到终端的任何输出

#!/bin/bash

# test_progress_bar3

Main () {

    tput civis                              # Turn off cursor
    for ((i=0; i<=100; i++)); do
        CurrLevel=$(./progress_bar3 "$i")   # Generate progress bar 0 to 100
        echo -ne "$CurrLevel"\\r            # Reprint overtop same line
        sleep .04
    done
    echo -e \\n                             # Advance line to keep last progress
    echo "$0 Done"
    tput cnorm                              # Turn cursor back on
} # Main

Main "$@"
请注意,该栏当前固定为100个字符宽。如果您想将其扩展到终端的大小,这是相当容易实现的,我的github页面上的更新版本也能很好地处理这一点

我将在这里发布我的脚本。 用法示例:

source ./progress_bar.sh
echo "This is some output"
setup_scroll_area
sleep 1
echo "This is some output 2"
draw_progress_bar 10
sleep 1
echo "This is some output 3"
draw_progress_bar 50
sleep 1
echo "This is some output 4"
draw_progress_bar 90
sleep 1
echo "This is some output 5"
destroy_scroll_area
我强烈推荐github上的脚本版本:

#!/bin/bash

# This code was inspired by the open source C code of the APT progress bar
# http://bazaar.launchpad.net/~ubuntu-branches/ubuntu/trusty/apt/trusty/view/head:/apt-pkg/install-progress.cc#L233

#
# Usage:
# Source this script
# setup_scroll_area
# draw_progress_bar 10
# draw_progress_bar 90
# destroy_scroll_area
#


CODE_SAVE_CURSOR="\033[s"
CODE_RESTORE_CURSOR="\033[u"
CODE_CURSOR_IN_SCROLL_AREA="\033[1A"
COLOR_FG="\e[30m"
COLOR_BG="\e[42m"
RESTORE_FG="\e[39m"
RESTORE_BG="\e[49m"

function setup_scroll_area() {
    lines=$(tput lines)
    let lines=$lines-1
    # Scroll down a bit to avoid visual glitch when the screen area shrinks by one row
    echo -en "\n"

    # Save cursor
    echo -en "$CODE_SAVE_CURSOR"
    # Set scroll region (this will place the cursor in the top left)
    echo -en "\033[0;${lines}r"

    # Restore cursor but ensure its inside the scrolling area
    echo -en "$CODE_RESTORE_CURSOR"
    echo -en "$CODE_CURSOR_IN_SCROLL_AREA"

    # Start empty progress bar
    draw_progress_bar 0
}

function destroy_scroll_area() {
    lines=$(tput lines)
    # Save cursor
    echo -en "$CODE_SAVE_CURSOR"
    # Set scroll region (this will place the cursor in the top left)
    echo -en "\033[0;${lines}r"

    # Restore cursor but ensure its inside the scrolling area
    echo -en "$CODE_RESTORE_CURSOR"
    echo -en "$CODE_CURSOR_IN_SCROLL_AREA"

    # We are done so clear the scroll bar
    clear_progress_bar

    # Scroll down a bit to avoid visual glitch when the screen area grows by one row
    echo -en "\n\n"
}

function draw_progress_bar() {
    percentage=$1
    lines=$(tput lines)
    let lines=$lines
    # Save cursor
    echo -en "$CODE_SAVE_CURSOR"

    # Move cursor position to last row
    echo -en "\033[${lines};0f"

    # Clear progress bar
    tput el

    # Draw progress bar
    print_bar_text $percentage

    # Restore cursor position
    echo -en "$CODE_RESTORE_CURSOR"
}

function clear_progress_bar() {
    lines=$(tput lines)
    let lines=$lines
    # Save cursor
    echo -en "$CODE_SAVE_CURSOR"

    # Move cursor position to last row
    echo -en "\033[${lines};0f"

    # clear progress bar
    tput el

    # Restore cursor position
    echo -en "$CODE_RESTORE_CURSOR"
}

function print_bar_text() {
    local percentage=$1

    # Prepare progress bar
    let remainder=100-$percentage
    progress_bar=$(echo -ne "["; echo -en "${COLOR_FG}${COLOR_BG}"; printf_new "#" $percentage; echo -en "${RESTORE_FG}${RESTORE_BG}"; printf_new "." $remainder; echo -ne "]");

    # Print progress bar
    if [ $1 -gt 99 ]
    then
        echo -ne "${progress_bar}"
    else
        echo -ne "${progress_bar}"
    fi
}

printf_new() {
    str=$1
    num=$2
    v=$(printf "%-${num}s" "$str")
    echo -ne "${v// /$str}"
}

这可以通过一种非常简单的方式实现:

使用for循环从0迭代到100 每一步睡眠25毫秒0.25秒 将另一个=符号附加到$bar变量,以使进度条更宽 回送进度条和百分比\r清除行并返回到行的开头-ne使echo不在末尾添加换行符并解析\r特殊字符 彩色进度条

要使进度条色彩鲜艳,可以使用格式转义序列-此处进度条为黄色:\e[43m,然后我们用\e[0m重置自定义设置,否则即使进度条完成,也会影响进一步的输入


我需要一个进度条来迭代csv文件中的行。我能够将cprn的代码改编成对我有用的东西:

BAR='##############################'
FILL='------------------------------'
totalLines=$(wc -l $file | awk '{print $1}')  # num. lines in file
barLen=30

# --- iterate over lines in csv file ---
count=0
while IFS=, read -r _ col1 col2 col3; do
    # update progress bar
    count=$(($count + 1))
    percent=$((($count * 100 / $totalLines * 100) / 100))
    i=$(($percent * $barLen / 100))
    echo -ne "\r[${BAR:0:$i}${FILL:$i:barLen}] $count/$totalLines ($percent%)"

    # other stuff
    (...)
done <$file

我需要一个进度条,可以 它在弹出的气泡消息notify send中表示电视音量级别。最近我一直在用python编写一个音乐播放器,电视画面大部分时间都是关闭的

来自终端的样本输出 Bash脚本 这是一条新行,告诉notify send立即替换最后一个弹出气泡:

-h string:x-canonical-private-synchronous:volume \


卷将弹出气泡消息分组在一起,此组中的新消息将立即替换以前的消息。您可以使用任何东西来代替音量。

根据echo手册页,至少在MacOS X sh/bash上,可以使用自己的内置echo命令,该命令不接受-n。。。因此,为了完成同样的任务,您需要将\r\c放在字符串的末尾,而不仅仅是\r\r输出的可移植方式是使用printf而不是echo。对于printf,我们必须使用以下格式:printf 50%\r,单引号不起作用,需要转义百分号。我不接受这一点,我猜这一操作将在未知硬件黑客上持续多长时间?在我看来,这是正确的答案,但will do Tothe的问题是,如何通过复制文件的示例来实现进度条。我关注的是图形问题,而不是计算文件复制操作的长度。这太棒了,我总是忘记通过pv进行传输:-我认为我的stat命令的工作方式有点不同,我的Linux版本的这个脚本:在执行过程中,另一个作业不能在后台启动,并且可能会被杀死而不是进程循环吗?我想你应该把它放在一个脚本中,这样只会捕获该脚本的一个出口。我喜欢这个命令,我正在我的文件中使用它。我只是有点不安,因为我真的不明白它是怎么工作的。第一行和第三行比较容易理解,但我还是不确定。我知道这是一个古老的答案,但有没有一种方法可以让我对编程新手有不同的解释这是唯一正确的答案,其他人只是编写101个玩具进度条,这些进度条毫无意义,对真正的、一次性的、不可跟踪的几乎所有程序都没有用处。谢谢。@Felipe,while循环是一个后台进程。美元!在第一个陷阱中,捕获该后台进程的进程id,并确保如果当前/父进程结束,则后台进程也会死亡,并且不会挂起。当长命令结束时,kill语句刚好结束后台进程;在/-\\\\\\;中为s执行;不打印F\r$s;睡眠:1;完成;完成*:睡眠可能需要INT而不是INTdecimals@Daenyth. 谢谢请告诉我们应该在哪里调用我们需要观察的命令,这是使用前面代码的过程?@goro:在一些工作中。。。上面一行;可以找到基于此有用答案和Adam Katz有用评论的更详细的讨论,重点是POSIX合规性。@AdamKatz:这是一个有用的、可移植的简化,但为了匹配Daenyth的方法,微调器必须基于\b而不是\r,因为它只在一行的开头工作:while:;在/-\\\\\;中对c执行do;不打印“%s\b”$c;睡眠1;完成;完成-或者,如果不希望在微调器后面显示光标:printf“”&&while:;在/-\\\\\;中对c执行do;打印F'\b%s'$c;睡眠1;完成;done@kaushal–Ctrl+C将手动停止。如果您有一个后台作业,则可以存储其PID作业=$!然后在kill-0$job 2>/dev/null时运行;do…,例如:sleep 15&job=$!;而kill-0$job 2>/dev/null;在/-\\\\\\;中为s执行;不打印F\r$s;睡眠:1;完成;不要放弃这个想法,只要你提前知道文件大小,你就可以通过这种方式提供比pv更高的附加值,但是盲目地发出pidof dd的信号是可怕的。试图用“我的PID处理是愚蠢的”来说明这一点,你也许可以捕获$!从dd开始,等待[[-e/proc/${dd_PID}]]。另请参阅控制逻辑背景示例,了解作业并执行某些操作直到完成。在编写脚本时,我们经常发现有一组要求非常有用。记录、显示进度、颜色、奇特的输出等。。。我一直觉得应该有一个简单的脚本框架。最后我决定实现一个,因为我找不到。你可能会发现这很有帮助。这是纯bash,我的意思是只是bash。这不应该移动到吗?我喜欢使用pv来处理任何可以通过管道传输的东西。示例:ssh remote cd/home/user/&tar czf-accounts | pv-s 23091k | tar xzc您能解释一下1.2.1.1下的行吗?您是否使用_fill和_empty变量执行sed替换?我很困惑,我没有使用sed,而是使用bash内部子字符串替换,因为这是一项简单的工作,我更喜欢使用bash的内部函数来完成这类工作。代码看起来也更好。:-选中此处并搜索子字符串替换。并且${u fill}被分配为${u done}个空格。这真漂亮。干得好。我肯定会在我所有的bash脚本中使用它

@恐惧面!为了提高速度,我做了一点调整,当进度没有从上一个值更改时跳过。含糖的按矩形更改破折号使其具有更专业的外观:printf\r进度:[${u fill///▇}${{{u empty//}]${{u progress}%%很好的解决方案,但是当你想压缩一个目录时,你该怎么做?很好!为了让它工作,我必须将行%u none=$100-$percent\u done更改为%u none=$100-$percent\u done我不明白这是如何集成到一些进程长度未知的进程中的。如果进程完成,如何停止进度条我认为用法应该是进度条,实际上是有吸引力的进度。如何将它与通过ssh在远程服务器上处理长期操作的功能联系起来?我的意思是,如何测量远程服务器上的升级时间?@faceless它不在您提供的代码范围内时间和它的价值down@Fusion这是一个unicode字符U+2587下八分之七块,对于现代shell来说应该是安全的。请尝试一下您的envsNeat和简单的函数。非常感谢!这就是我正在搜索的!非常感谢:您可以使用pv或bar来可视化不同进程的进度,例如计时器倒计时、在tex中的位置吗t文件、您的应用程序安装、运行时设置等?我认为您的示例没有更多的选项。它似乎通过管道将tqdm标准输出传递给wc-l。您可能希望避免这种情况。@cprn tqdm将在将其输入标准输入传输到标准输出的同时显示标准输出的进度。在这种情况下,wc-l将接收相同的输入,就好像不包含tqdm一样ed.啊,现在有道理了。谢谢你的解释。+1是最简单的方法!如果你看到没有打印点,试着减少数字,例如-checkpoint=.10。当使用tar-xz提取时,它也非常有效。对于那些想要最简单的东西的人,我刚刚用cprn first answer制作了我的。这是一个使用som的函数中非常简单的进度条用愚蠢的比例规则来划分界限:@YCN-我发现自己使用您的实现的次数比我愿意承认的要多。谢谢,伙计!xDPerfect!正是我所寻找的解决方案!按我的需要工作。非常好!谢谢。
#!/bin/sh
src_size=$(gzip -l "$src" | tail -n1 | tr -s ' ' | cut -d' ' -f3)
#!/bin/sh

BAR_length=50
BAR_character='#'
BAR=$(printf "%${BAR_length}s" | tr ' ' $BAR_character)

work_todo=$(get_work_todo)             # how much there is to do

while true; do
    work_done=$(get_work_done)         # how much has been done so far
    i=$(( $work_done * $BAR_length / $work_todo ))
    echo -ne "\r${BAR:0:$i}"
    if [ $work_done == $work_todo ]; then
        echo ""
        break;
    fi
    sleep .1
done
#!/bin/sh

src=/path/to/source/file
tgt=/path/to/target/file

function get_work_todo() {
    echo $(stat -c%s "$src")
}

function get_work_done() {
    [ -e "$tgt" ] &&                   # if target file exists
        echo $(stat -c%s "$tgt") ||    # echo its size, else
        echo 0                         # echo zero
}

cp "$src" "$tgt" &                     # copy in the background

source /usr/lib/progress_bar.sh        # execute the progress bar
#!/bin/bash
#
# Progress bar by Adriano Pinaffo
# Available at https://github.com/adriano-pinaffo/progressbar.sh
# Inspired on work by Edouard Lopez (https://github.com/edouard-lopez/progress-bar.sh)
# Version 1.0
# Date April, 28th 2017

function error {
  echo "Usage: $0 [SECONDS]"
  case $1 in
    1) echo "Pass one argument only"
    exit 1
    ;;
    2) echo "Parameter must be a number"
    exit 2
    ;;
    *) echo "Unknown error"
    exit 999
  esac
}

[[ $# -ne 1 ]] && error 1
[[ $1 =~ ^[0-9]+$ ]] || error 2

duration=${1}
barsize=$((`tput cols` - 7))
unity=$(($barsize / $duration))
increment=$(($barsize%$duration))
skip=$(($duration/($duration-$increment)))
curr_bar=0
prev_bar=
for (( elapsed=1; elapsed<=$duration; elapsed++ ))
do
  # Elapsed
prev_bar=$curr_bar
  let curr_bar+=$unity
  [[ $increment -eq 0 ]] || {  
    [[ $skip -eq 1 ]] &&
      { [[ $(($elapsed%($duration/$increment))) -eq 0 ]] && let curr_bar++; } ||
    { [[ $(($elapsed%$skip)) -ne 0 ]] && let curr_bar++; }
  }
  [[ $elapsed -eq 1 && $increment -eq 1 && $skip -ne 1 ]] && let curr_bar++
  [[ $(($barsize-$curr_bar)) -eq 1 ]] && let curr_bar++
  [[ $curr_bar -lt $barsize ]] || curr_bar=$barsize
  for (( filled=0; filled<=$curr_bar; filled++ )); do
    printf "▇"
  done

  # Remaining
  for (( remain=$curr_bar; remain<$barsize; remain++ )); do
    printf " "
  done

  # Percentage
  printf "| %s%%" $(( ($elapsed*100)/$duration))

  # Return
  sleep 1
  printf "\r"
done
printf "\n"
exit 0
#!/usr/bin/env bash

main() {
  for (( i = 0; i <= 100; i=$i + 1)); do
    progress_bar "$i"
    sleep 0.1;
  done
  progress_bar "done"
  exit 0
}

progress_bar() {
  if [ "$1" == "done" ]; then
    spinner="X"
    percent_done="100"
    progress_message="Done!"
    new_line="\n"
  else
    spinner='/-\|'
    percent_done="${1:-0}"
    progress_message="$percent_done %"
  fi

  percent_none="$(( 100 - $percent_done ))"
  [ "$percent_done" -gt 0 ] && local done_bar="$(printf '#%.0s' $(seq -s ' ' 1 $percent_done))"
  [ "$percent_none" -gt 0 ] && local none_bar="$(printf '~%.0s' $(seq -s ' ' 1 $percent_none))"

  # print the progress bar to the screen
  printf "\r Progress: [%s%s] %s %s${new_line}" \
    "$done_bar" \
    "$none_bar" \
    "${spinner:x++%${#spinner}:1}" \
    "$progress_message"
}

main "$@"
[##################################################] 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"
}
# 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"
source ./progress_bar.sh
echo "This is some output"
setup_scroll_area
sleep 1
echo "This is some output 2"
draw_progress_bar 10
sleep 1
echo "This is some output 3"
draw_progress_bar 50
sleep 1
echo "This is some output 4"
draw_progress_bar 90
sleep 1
echo "This is some output 5"
destroy_scroll_area
#!/bin/bash

# This code was inspired by the open source C code of the APT progress bar
# http://bazaar.launchpad.net/~ubuntu-branches/ubuntu/trusty/apt/trusty/view/head:/apt-pkg/install-progress.cc#L233

#
# Usage:
# Source this script
# setup_scroll_area
# draw_progress_bar 10
# draw_progress_bar 90
# destroy_scroll_area
#


CODE_SAVE_CURSOR="\033[s"
CODE_RESTORE_CURSOR="\033[u"
CODE_CURSOR_IN_SCROLL_AREA="\033[1A"
COLOR_FG="\e[30m"
COLOR_BG="\e[42m"
RESTORE_FG="\e[39m"
RESTORE_BG="\e[49m"

function setup_scroll_area() {
    lines=$(tput lines)
    let lines=$lines-1
    # Scroll down a bit to avoid visual glitch when the screen area shrinks by one row
    echo -en "\n"

    # Save cursor
    echo -en "$CODE_SAVE_CURSOR"
    # Set scroll region (this will place the cursor in the top left)
    echo -en "\033[0;${lines}r"

    # Restore cursor but ensure its inside the scrolling area
    echo -en "$CODE_RESTORE_CURSOR"
    echo -en "$CODE_CURSOR_IN_SCROLL_AREA"

    # Start empty progress bar
    draw_progress_bar 0
}

function destroy_scroll_area() {
    lines=$(tput lines)
    # Save cursor
    echo -en "$CODE_SAVE_CURSOR"
    # Set scroll region (this will place the cursor in the top left)
    echo -en "\033[0;${lines}r"

    # Restore cursor but ensure its inside the scrolling area
    echo -en "$CODE_RESTORE_CURSOR"
    echo -en "$CODE_CURSOR_IN_SCROLL_AREA"

    # We are done so clear the scroll bar
    clear_progress_bar

    # Scroll down a bit to avoid visual glitch when the screen area grows by one row
    echo -en "\n\n"
}

function draw_progress_bar() {
    percentage=$1
    lines=$(tput lines)
    let lines=$lines
    # Save cursor
    echo -en "$CODE_SAVE_CURSOR"

    # Move cursor position to last row
    echo -en "\033[${lines};0f"

    # Clear progress bar
    tput el

    # Draw progress bar
    print_bar_text $percentage

    # Restore cursor position
    echo -en "$CODE_RESTORE_CURSOR"
}

function clear_progress_bar() {
    lines=$(tput lines)
    let lines=$lines
    # Save cursor
    echo -en "$CODE_SAVE_CURSOR"

    # Move cursor position to last row
    echo -en "\033[${lines};0f"

    # clear progress bar
    tput el

    # Restore cursor position
    echo -en "$CODE_RESTORE_CURSOR"
}

function print_bar_text() {
    local percentage=$1

    # Prepare progress bar
    let remainder=100-$percentage
    progress_bar=$(echo -ne "["; echo -en "${COLOR_FG}${COLOR_BG}"; printf_new "#" $percentage; echo -en "${RESTORE_FG}${RESTORE_BG}"; printf_new "." $remainder; echo -ne "]");

    # Print progress bar
    if [ $1 -gt 99 ]
    then
        echo -ne "${progress_bar}"
    else
        echo -ne "${progress_bar}"
    fi
}

printf_new() {
    str=$1
    num=$2
    v=$(printf "%-${num}s" "$str")
    echo -ne "${v// /$str}"
}
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"
}
BAR='##############################'
FILL='------------------------------'
totalLines=$(wc -l $file | awk '{print $1}')  # num. lines in file
barLen=30

# --- iterate over lines in csv file ---
count=0
while IFS=, read -r _ col1 col2 col3; do
    # update progress bar
    count=$(($count + 1))
    percent=$((($count * 100 / $totalLines * 100) / 100))
    i=$(($percent * $barLen / 100))
    echo -ne "\r[${BAR:0:$i}${FILL:$i:barLen}] $count/$totalLines ($percent%)"

    # other stuff
    (...)
done <$file
[##----------------------------] 17128/218210 (7%)
#!/bin/bash

# Show a progress bar at step number $1 (from 0 to 100)


function is_int() { test "$@" -eq "$@" 2> /dev/null; } 

# Parameter 1 must be integer
if ! is_int "$1" ; then
   echo "Not an integer: ${1}"
   exit 1
fi

# Parameter 1 must be >= 0 and <= 100
if [ "$1" -ge 0 ] && [ "$1" -le 100 ]  2>/dev/null
then
    :
else
    echo bad volume: ${1}
    exit 1
fi

# Main function designed for quickly copying to another program 
Main () {

    Bar=""                      # Progress Bar / Volume level
    Len=25                      # Length of Progress Bar / Volume level
    Div=4                       # Divisor into Volume for # of blocks
    Fill="▒"                    # Fill up to $Len
    Arr=( "▉" "▎" "▌" "▊" )     # UTF-8 left blocks: 7/8, 1/4, 1/2, 3/4

    FullBlock=$((${1} / Div))   # Number of full blocks
    PartBlock=$((${1} % Div))   # Size of partial block (array index)

    while [[ $FullBlock -gt 0 ]]; do
        Bar="$Bar${Arr[0]}"     # Add 1 full block into Progress Bar
        (( FullBlock-- ))       # Decrement full blocks counter
    done

    # If remainder zero no partial block, else append character from array
    if [[ $PartBlock -gt 0 ]]; then
        Bar="$Bar${Arr[$PartBlock]}"
    fi

    while [[ "${#Bar}" -lt "$Len" ]]; do
        Bar="$Bar$Fill"         # Pad Progress Bar with fill character
    done

    echo Volume: "$1 $Bar"
    exit 0                      # Remove this line when copying into program
} # Main

Main "$@"
#!/bin/bash

# test_progress_bar3

Main () {

    tput civis                              # Turn off cursor
    for ((i=0; i<=100; i++)); do
        CurrLevel=$(./progress_bar3 "$i")   # Generate progress bar 0 to 100
        echo -ne "$CurrLevel"\\r            # Reprint overtop same line
        sleep .04
    done
    echo -e \\n                             # Advance line to keep last progress
    echo "$0 Done"
    tput cnorm                              # Turn cursor back on
} # Main

Main "$@"
VolumeBar $CurrVolume
# Ask Ubuntu: https://askubuntu.com/a/871207/307523
notify-send --urgency=critical "tvpowered" \
    -h string:x-canonical-private-synchronous:volume \
    --icon=/usr/share/icons/gnome/48x48/devices/audio-speakers.png \
    "Volume: $CurrVolume $Bar"
-h string:x-canonical-private-synchronous:volume \