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

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

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


当前回答

要制作一个tar进度条

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

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

你什么都不需要了。

其他回答

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

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

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

在我的系统上使用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

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}"
}
#!/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”设置为预计输出文件大小)

我以“恐惧之边”提供的答案为基础

它连接到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'