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

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

你建议用什么方法向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

其他回答

要制作一个tar进度条

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

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

你什么都不需要了。

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

/usr/bin/dd的SIGUSR1信号处理特性。 基本上,如果您发送'kill SIGUSR1 $(pid_of_running_dd_process)',它将输出 吞吐量速度和传输量的摘要。 后台dd,然后定期查询它的更新,并生成 像老式的FTP客户端一样。 使用/dev/stdout作为非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

我需要一个进度条,将适合弹出气泡消息(通知-发送),以代表电视音量水平。最近我一直在用python写一个音乐播放器,而电视画面大部分时间都是关闭的。

终端输出样本


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="▒"                    # 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 "$@"

测试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将弹出气泡消息快速发送到桌面。这是必需的,因为音量级别可以在一秒钟内更改多次,并且默认的气泡消息行为是让消息在桌面上停留许多秒。

示例弹出气泡消息

弹出气泡消息bash代码

从上面的脚本中,主函数被复制到一个名为tvpowered的现有bash脚本中名为VolumeBar的新函数。已删除复制的主函数中的exit 0命令。

下面是如何调用它并让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 \

卷将弹出气泡消息分组在一起,该组中的新消息立即替换前一个消息。你可以用任何东西来代替体积。

我今天有同样的事情要做,根据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

我需要一个进度条来迭代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%)