496

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

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

您推荐哪些方式向 shell 脚本添加进度条?

4

42 回答 42

766

您可以通过覆盖一行来实现这一点。用于\r返回行首而不写入\n终端。

完成后写下\n以推进该行。

用于echo -ne

  1. 不打印\n
  2. 识别转义序列,如\r.

这是一个演示:

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

在下面的评论中,puk 提到如果您从长行开始然后想写短行,则此“失败”:在这种情况下,您需要覆盖长行的长度(例如,使用空格)。

于 2008-10-26T14:47:53.280 回答
106

您可能还对如何做微调器感兴趣:

我可以在 Bash 中做一个微调器吗?

当然!

i=1
sp="/-\|"
echo -n ' '
while true
do
    printf "\b${sp:i++%${#sp}:1}"
done

每次循环迭代时,它都会显示 sp 字符串中的下一个字符,并在到达末尾时回绕。(i 是要显示的当前字符的位置,${#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
于 2010-07-25T20:12:20.817 回答
67

得到了我前几天写的一个简单的进度条功能:

#!/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/获取它

于 2015-01-20T12:05:57.323 回答
55

使用 Linux 命令pv

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

于 2011-07-28T17:35:46.257 回答
49

一些帖子显示了如何显示命令的进度。为了计算它,你需要看看你进步了多少。在 BSD 系统上,一些命令,例如 dd(1),接受一个SIGINFO信号,并报告它们的进度。在 Linux 系统上,一些命令的响应与SIGUSR1. 如果此工具可用,您可以通过管道传输您的输入dd以监控处理的字节数。

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

$ pmonitor -c gzip
/home/dds/data/mysql-2015-04-01.sql.gz 58.06%

我的博客(“Monitor Process Progress on Unix”)上出现了早期版本的 Linux 和 FreeBSD shell 脚本。

于 2008-10-26T15:18:26.010 回答
45

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

预览

progress-bar.sh 在行动

来源

我把它放在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
于 2016-10-06T14:17:54.930 回答
28

还没有看到任何类似的东西,这里的所有自定义功能似乎都只专注于渲染,所以......我非常简单的符合 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- 打印到终端(即到stdout
  • echo -n- 最后打印不换行
  • echo -e- 打印时解释特殊字符
  • "\r"- 回车,返回行首的特殊字符

您可以让它以任何速度渲染任何内容,因此这种方法非常通用,例如,经常用于可视化愚蠢电影中的“黑客行为”,不是开玩笑。

完整答案(从零到工作示例)

问题的核心是如何确定$i值,即显示多少进度条。在上面的示例中,我只是让它在for循环中递增以说明原理,但现实生活中的应用程序将使用无限循环并$i在每次迭代时计算变量。为了进行上述计算,它需要以下成分:

  1. 有多少工作要做
  2. 到目前为止已经完成了多少工作

如果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
  • foo=$(bar)-bar在子进程中运行并将其保存stdout$foo
  • stat- 打印文件统计到stdout
  • stat -c- 打印一个格式化的值
  • %s- 总大小的格式

在文件解包等操作的情况下,计算源大小会稍微困难一些,但仍然像获取未压缩文件的大小一样容易:

#!/bin/sh
src_size=$(gzip -l "$src" | tail -n1 | tr -s ' ' | cut -d' ' -f3)
  • gzip -l- 打印有关 zip 存档的信息
  • tail -n1- 从底部开始使用 1 行
  • tr -s ' '- 将多个空格转换为一个(“挤压”它们)
  • cut -d' ' -f3- 剪切第三个空格分隔的字段(列)

这是我之前提到的问题的实质。这种解决方案越来越不通用。实际进度的所有计算都与您尝试可视化的域紧密相关,它是单个文件操作、计时器倒计时、目录中文件数量的增加、对多个文件的操作等等,因此,它不能重复使用。唯一可重用的部分是进度条渲染。要重用它,您需要将其抽象并保存在文件中(例如/usr/lib/progress_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

显然,您可以将其包装在一个函数中,重写以使用管道流,获取分叉的进程 ID$!并将其传递给它,progress_bar.sh以便它可以猜测如何计算要完成的工作和完成的工作,无论您的毒药是什么。

旁注

我经常被问到这两个问题:

  1. ${}: 在上面的例子中我使用${foo:A:B}. 这种语法的技术术语是Parameter Expansion,一种内置的 shell 功能,允许操作变量(参数),例如修剪字符串:但也可以做其他事情 - 它不会产生子 shell。我能想到的参数扩展最突出的描述(不完全兼容 POSIX,但可以让读者很好地理解这个概念)在man bash页面中。
  2. $(): 在上面的例子中我使用foo=$(bar). 它在子进程(又名Subshel​​l )中生成一个单独的 shell,在其中运行bar命令并将其标准输出分配给一个$foo变量。它与Process Substitution不同,它与管道( )完全不同|。最重要的是,它有效。有人说这应该避免,因为它很慢。我认为这在这里“没问题”,因为无论这段代码试图可视化的时间都足够长,以至于需要一个进度条。换句话说,子壳不是瓶颈。调用 subshel​​l 也让我不必解释为什么return不是大多数人认为的那样,什么是退出状态以及为什么从 shell 中的函数传递值并不是 shell 函数通常擅长的。要了解有关所有内容的更多信息,我再次强烈推荐该man bash页面。
于 2016-12-01T01:21:15.500 回答
18

GNU tar有一个有用的选项,它提供了一个简单的进度条功能。

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

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

可以通过以下方式获得相同的效果:

$ tar -c --checkpoint=.1000 /var
于 2010-08-02T14:15:23.667 回答
16

我也想贡献我自己的进度条

它通过使用Half unicode块来实现子字符精度

在此处输入图像描述

包含代码

于 2017-07-19T08:12:30.043 回答
16

APT 风格的进度条(不破坏正常输出)

在此处输入图像描述

编辑:有关更新版本,请查看我的github 页面

我对这个问题的回答不满意。我个人一直在寻找的是 APT 所见的精美进度条。

我查看了 APT 的 C 源代码,并决定为 bash 编写我自己的等价物。

这个进度条将很好地留在终端的底部,并且不会干扰发送到终端的任何输出。

请注意,该栏目前固定为 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}"
}
于 2018-12-04T15:49:39.343 回答
14

使用 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
于 2010-07-25T20:07:26.520 回答
11

这使您可以看到命令仍在执行:

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

这将创建一个无限循环,在后台执行并回显“。” 每一秒。这将显示.在外壳中。运行tar命令或您想要的任何命令。当该命令完成执行时,然后终止在后台运行的最后一个作业 - 这是无限的 while 循环

于 2013-05-02T21:54:03.307 回答
10

这是它的外观

上传文件

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

使用示例

在这里,我们上传一个文件并在每次迭代时重绘进度条。只要我们能得到 2 个值:最大值和当前值,实际执行什么工作并不重要。

在下面的示例中,最大值是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"
于 2018-09-30T20:24:12.290 回答
8
于 2021-07-08T08:30:03.500 回答
6

我需要一个进度条来遍历 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

看起来像这样:

[##----------------------------] 17128/218210 (7%)
于 2020-11-20T15:39:05.630 回答
5

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

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

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

于 2008-10-26T14:42:52.167 回答
4

我的解决方案显示当前正在解压缩和写入的 tarball 的百分比。我在写出 2GB 根文件系统映像时使用它。你真的需要一个进度条来处理这些事情。我所做的是 gzip --list用来获取压缩包的总未压缩大小。从中我计算出将文件分成 100 个部分所需的阻塞因子。最后,我为每个块打印一条检查点消息。对于一个 2GB 的文件,这提供了大约 10MB 个块。如果它太大,那么您可以将 BLOCKING_FACTOR 除以 10 或 100,但是很难以百分比形式打印漂亮的输出。

假设您使用的是 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}
}
于 2010-08-24T09:01:34.087 回答
4

首先bar不是唯一的一个pipe进度表。另一个(可能更知名)是 pv(管道查看器)。

其次 bar 和 pv 可以像这样使用:

$ bar file1 | wc -l 
$ pv file1 | wc -l

甚至:

$ tail -n 100 file1 | bar | wc -l
$ tail -n 100 file1 | pv | wc -l

如果您想在处理参数中给出的文件的命令中使用 bar 和 pv ,一个有用的技巧是使用进程替换

$ copy <(bar file1) file2
$ copy <(pv file1) file2

进程替换是一种 bash 魔术,它创建临时 fifo 管道文件 /dev/fd/ 并通过此管道从已运行的进程(括号内)连接标准输出,并且副本就像普通文件一样看到它(除了一个例外,它只能读取它前锋)。

更新:

bar 命令本身也允许复制。人吧之后:

bar --in-file /dev/rmt/1cbn --out-file \
     tape-restore.tar --size 2.4g --buffer-size 64k

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

于 2014-07-18T06:06:44.843 回答
4

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

在此处输入图像描述

它也发布在Git Hub上。

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

享受

于 2017-04-29T06:05:36.150 回答
3

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

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

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

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

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

所以我可以说:

Wait "34 min" "warm up the oven"

或者

Wait "dec 31" "happy new year"

于 2016-01-10T17:40:47.220 回答
3

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

于 2016-04-08T07:21:21.990 回答
2

对我来说最容易使用和最好看的命令是命令pv或者bar像一些人已经写过的

例如:需要对整个驱动器进行备份dd

通常你使用dd if="$input_drive_path" of="$output_file_path"

pv你一起可以这样:

dd if="$input_drive_path" | pv | dd of="$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
于 2014-11-04T13:02:23.977 回答
2

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

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 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;

或者

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;

或者

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 循环中使用标志/变量来检查和显示进度的值/范围。

于 2015-05-26T09:04:50.240 回答
2

https://github.com/extensionsapp/progre.sh

创建 40% 的进度:progreSh 40

在此处输入图像描述

于 2018-11-01T02:04:21.283 回答
2

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

  • for使用循环从 0 迭代到 100
  • 每步睡眠 25 毫秒(0.25 秒)
  • 将另一个符号附加到$bar变量=以使进度条更宽
  • 回显进度条和百分比(\r清除行并返回到行首;-nemakeecho不会在末尾添加换行符并解析\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,否则即使进度条完成也会影响进一步的输入。

自定义进度条

于 2020-07-26T14:55:11.993 回答
2

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

终端的示例输出

test_progress_bar3.gif


bash 脚本

#!/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="▒&quot;                    # Fill up to $Len
    Arr=( "▉&quot; "▎&quot; "▌&quot; "▊&quot; )     # 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 "$@"

测试 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 "$@"

TL;博士

本节详细介绍如何notify-send将垃圾邮件弹出气泡消息快速发送到桌面。这是必需的,因为音量级别可以在一秒钟内更改多次,并且默认的气泡消息行为是让消息在桌面上停留数秒。

示例弹出气泡消息

电视供电的.gif

弹出气泡消息 bash 代码

从上面的脚本中,该main函数被复制到一个VolumeBar名为tvpowered. exit 0复制函数中的命令main已删除。

以下是如何调用它并让 Ubuntu 的notify-send命令知道我们将发送垃圾邮件弹出气泡消息:

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"

这是告诉notify-send立即替换最后一个弹出气泡的新行:

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

volume将弹出气泡消息组合在一起,该组中的新消息立即替换以前的消息。您可以使用anything而不是volume.

于 2021-01-01T18:37:26.170 回答
1

这仅适用于使用 gnome zenity。Zenity 为 bash 脚本提供了一个很棒的原生界面: https ://help.gnome.org/users/zenity/stable/

来自 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
于 2014-03-17T12:25:41.193 回答
1

我使用了在 shell script 中创建重复字符的字符串以进行字符重复的答案。对于需要显示进度条的脚本,我有两个相对较小的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 个“#”字符的 bar 准备字符串,然后:

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 上的进度条,只需>&2在每个 printf 命令的末尾添加即可。

于 2015-06-29T20:56:33.000 回答
1

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

#!/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 "$@"
于 2018-01-22T20:05:11.807 回答
1

具有随机颜色的灵活版本,用于操作和日期的字符串。

function spinner() {
  local PID="$1"
  local str="${2:-Processing!}"
  local delay="0.1"
  # tput civis  # hide cursor
  while ( kill -0 $PID 2>/dev/null )
    do
      printf "\e[38;5;$((RANDOM%257))m%s\r\e[0m" "[$(date '+%d/%m/%Y %H:%M:%S')][      $str      ]"; sleep "$delay"
      printf "\e[38;5;$((RANDOM%257))m%s\r\e[0m" "[$(date '+%d/%m/%Y %H:%M:%S')][      $str      ]"; sleep "$delay"
      printf "\e[38;5;$((RANDOM%257))m%s\r\e[0m" "[$(date '+%d/%m/%Y %H:%M:%S')][      $str      ]"; sleep "$delay"
  done
  printf "\e[38;5;$((RANDOM%257))m%s\r\e[0m" "[$(date '+%d/%m/%Y %H:%M:%S')][ ✅  ✅  ✅   Done!   ✅  ✅  ✅ ]"; sleep "$delay"
  # tput cnorm  # restore cursor

  return 0
}

用法:

# your long running proccess pushed to the background
sleep 20 &

# spinner capture-previous-proccess-id string
spinner $! 'Working!'

输出示例:

[04/06/2020 03:22:24][      Seeding!      ]
于 2020-06-04T01:23:39.657 回答
0

我为嵌入式系统做了一个纯 shell 版本,利用:

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

    基本上,如果您发送“kill SIGUSR1 $(pid_of_running_dd_process)”,它将输出吞吐速度和传输量的摘要。

  • 后台 dd 然后定期查询它的更新,并像老式的 ftp 客户端那样生成哈希滴答。

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

最终结果允许您进行任何文件传输操作并获得看起来像老式 FTP 'hash' 输出的进度更新,您只需为每 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
于 2014-10-10T01:03:13.430 回答
0

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

然后,假设您将 Python 脚本保存为progressbar.py,可以通过运行以下命令从 bash 脚本中显示进度条:

python progressbar.py 10 50

它会显示一个进度条大小的50字符并“运行”10几秒钟。

于 2015-03-27T09:45:51.667 回答
0

我建立在恐惧边提供的答案之上

这将连接到 Oracle 数据库以检索 RMAN 还原的进度。

#!/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}%%"

}

function rman_check {
sqlplus -s / as sysdba <<EOF
set heading off
set feedback off
select
round((sofar/totalwork) * 100,0) pct_done
from
v\$session_longops
where
totalwork > sofar
AND
opname NOT LIKE '%aggregate%'
AND
opname like 'RMAN%';
exit
EOF
}

# Variables
_start=1

# This accounts as the "totalState" variable for the ProgressBar function
_end=100

_rman_progress=$(rman_check)
#echo ${_rman_progress}

# Proof of concept
#for number in $(seq ${_start} ${_end})

while [ ${_rman_progress} -lt 100 ]
do

for number in _rman_progress
do
sleep 10
ProgressBar ${number} ${_end}
done

_rman_progress=$(rman_check)

done
printf '\nFinished!\n'
于 2016-02-10T11:54:02.017 回答
0
#!/bin/bash

function progress_bar() {
    bar=""
    total=10
    [[ -z $1 ]] && input=0 || input=${1}
    x="##"
   for i in `seq 1 10`; do
        if [ $i -le $input ] ;then
            bar=$bar$x
        else
            bar="$bar  "
       fi
    done
    #pct=$((200*$input/$total % 2 + 100*$input/$total))
    pct=$(($input*10))
    echo -ne "Progress : [ ${bar} ] (${pct}%) \r"    
    sleep 1
    if [ $input -eq 10 ] ;then
        echo -ne '\n'
    fi

}

可以创建一个函数,以 1-10 的比例绘制柱状图:

progress_bar 1
echo "doing something ..."
progress_bar 2
echo "doing something ..."
progress_bar 3
echo "doing something ..."
progress_bar 8
echo "doing something ..."
progress_bar 10
于 2017-08-14T14:19:52.127 回答
0
#!/bin/bash
tot=$(wc -c /proc/$$/fd/255 | awk '/ /{print $1}')
now() {
echo $(( 100* ($(awk '/^pos:/{print $2}' < /proc/$$/fdinfo/255)-166) / (tot-166) )) "%"
}
now;
now;
now;
now;
now;
now;
now;
now;
now;

输出:

0 %
12 %
25 %
37 %
50 %
62 %
75 %
87 %
100 %

注意:如果您输入 1 而不是 255,您将监视标准输入...使用 2 标准输出(但您必须修改源以将“tot”设置为预计的输出文件大小)

于 2018-12-11T08:41:58.430 回答
0

关于这个主题有很多不同的答案,但是在计算文本文件操作的百分比时,使用current length / total size方式,例如显示ver_big_file.json进度百分比,我建议awk为此目的使用,如下面的代码:

awk '
    function bar(x){s="";i=0;while (i++ < x) s=s "#";return s}
    BEGIN{
        ("ls -l " ARGV[1]) | getline total;
        split(total,array);
        total=array[5];
    }
    {
        cur+=length($0)+1;
        percent=int(cur / total * 100);
        printf "LINE %s:%s %s%%\r", NR, bar(percent*.8), percent 
    }
    END {print}' very_big_file.json | grep "keyword" | ...

这种方式非常精确,基于流,但它只适用于文本文件。

于 2020-09-14T02:14:16.310 回答
-1

这是 nExace 用于 bash 脚本的迷幻进度条。它可以从命令行调用为“./progressbar x y”,其中“x”是以秒为单位的时间,“y”是与该部分进度相关联的消息。

如果您希望脚本的其他部分控制进度条,则内部 progressbar() 函数本身也很好。例如,发送 'progressbar 10 "Creating directory tree";' 将显示:

[#######                                     ] (10%) Creating directory tree

当然,它会很好迷幻……

#!/bin/bash

if [ "$#" -eq 0 ]; then echo "x is \"time in seconds\" and z is \"message\""; echo "Usage: progressbar x z"; exit; fi
progressbar() {
        local loca=$1; local loca2=$2;
        declare -a bgcolors; declare -a fgcolors;
        for i in {40..46} {100..106}; do
                bgcolors+=("$i")
        done
        for i in {30..36} {90..96}; do
                fgcolors+=("$i")
        done
        local u=$(( 50 - loca ));
        local y; local t;
        local z; z=$(printf '%*s' "$u");
        local w=$(( loca * 2 ));
        local bouncer=".oO°Oo.";
        for ((i=0;i<loca;i++)); do
                t="${bouncer:((i%${#bouncer})):1}"
                bgcolor="\\E[${bgcolors[RANDOM % 14]}m \\033[m"
                y+="$bgcolor";
        done
        fgcolor="\\E[${fgcolors[RANDOM % 14]}m"
        echo -ne " $fgcolor$t$y$z$fgcolor$t \\E[96m(\\E[36m$w%\\E[96m)\\E[92m $fgcolor$loca2\\033[m\r"
};
timeprogress() {
        local loca="$1"; local loca2="$2";
        loca=$(bc -l <<< scale=2\;"$loca/50")
        for i in {1..50}; do
                progressbar "$i" "$loca2";
                sleep "$loca";
        done
        printf "\n"
};
timeprogress "$1" "$2"
于 2016-05-17T19:56:59.357 回答
-1

先将进程执行到后台,然后经常观察它的运行状态,正在运行的打印花样,再次检查它的状态是否在运行;

使用 while 循环经常查看进程的状态。

使用 pgrep 或任何其他命令来监视和获取进程的运行状态。

如果使用 pgrep 根据需要将不必要的输出重定向到 /dev/null。

代码:

sleep 12&
while pgrep sleep &> /dev/null;do echo -en "#";sleep 0.5;done

这个“#”会一直打印到sleep终止,这个方法用来实现程序进度条的进度条。

您还可以使用此方法对 shell 脚本的命令进行可视化分析它的处理时间。

BUG: 此 pgrep 方法并非在所有情况下都有效,意外地另一个进程以相同的名称运行,while 循环没有结束。

所以通过指定它的PID来获取进程的运行状态,使用may进程可以使用一些命令,

命令ps a将列出所有具有 id 的进程,你需要grep找出指定进程的 pid

于 2016-06-13T10:23:43.850 回答
-1

我想根据命令输出的行数与上一次运行的目标行数来跟踪进度:

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

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

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

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

顺便说一句:我在 Mac OSX 上使用 bash。我将此代码基于mariascio的微调器。

于 2016-07-08T23:59:02.297 回答
-3

制作 tar 进度条

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

其中“19”是 tar 中的文件数除以预期进度条的长度。示例:.tgz 包含 140 个文件,您需要 76 个“.”的进度条,您可以输入 -L 2。

你不需要其他任何东西。

于 2012-05-30T10:40:40.520 回答
-3

有一次,我还有一个忙碌的脚本被占用了几个小时而没有显示任何进展。所以我实现了一个功能,主要包括前面答案的技术:

#!/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
于 2013-11-01T11:52:12.897 回答
-5

我今天也有同样的事情要做,根据 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
于 2013-05-02T11:40:11.703 回答