在bash或其他* NIX中运行脚本时,如果要运行的命令需要花费几秒钟以上的时间,则需要使用进度条。
例如,拷贝大文件,打开大的tar文件等。
您推荐使用哪些方法来为shell脚本添加进度条?
在bash或其他* NIX中运行脚本时,如果要运行的命令需要花费几秒钟以上的时间,则需要使用进度条。
例如,拷贝大文件,打开大的tar文件等。
您推荐使用哪些方法来为shell脚本添加进度条?
使用管道查看(pv)工具,在我的系统上可以使用更简单的方法。
srcdir=$1
outfile=$2
tar -Ocf - $srcdir | pv -i 1 -w 50 -berps `du -bs $srcdir | awk '{print $1}'` | 7za a -si $outfile
上传文件
[##################################################] 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"
这样可以让您直观地看到一个命令仍在执行中:
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
这将创建一个无限循环,后台执行并每秒回显一个“.”。这将在shell中显示.
运行tar
命令或任何您想要的命令。当该命令完成执行时,杀死在后台运行的最后一个作业 - 即无限循环。
pid=$!
),并在陷阱声明中稍后使用该变量。 - jarno我需要一条进度栏来迭代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%)
我需要一个进度条,可适用于弹出的气泡消息(notify-send
),以代表电视音量水平。最近,我在使用Python编写音乐播放器,电视屏幕大部分时间都是关闭状态。
#!/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 "$@"
本节详细介绍如何使用notify-send
快速向桌面发送弹出气泡消息。这是必需的,因为音量级别可能每秒钟变化多次,默认的气泡消息行为是让消息在桌面停留多秒钟。
从上面的脚本中,将main
函数复制到名为VolumeBar
的现有bash脚本中。复制后的main
函数中的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 \
volume
组合了弹出式气泡消息,并将该组中的新消息立即替换掉先前的消息。您可以使用 anything
代替 volume
。
基于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
享受
'\r' + $some_sort_of_progress_msg
。 问题在于每秒打印数百个更新会减慢进程速度。7z a -r newZipFile myFolder
将在压缩时输出每个文件名),则存在一种更简单,更快,无痛和可定制的解决方案。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
tqdm
STDOUT传递给wc -l
。你可能想要转义它。 - cprntqdm
会在将其输入从 STDIN
管道传输到 STDOUT
的同时,在 STDERR
上显示进度。 在这种情况下,wc -l
将接收与未包含 tqdm
时相同的输入。 - casper.dclgzip --list
获取tarball的总未压缩大小。然后,我计算出需要分成100个部分的阻塞因子。最后,我为每个块打印检查点消息。对于一个2GB的文件,这大约给出了10MB一个块。如果太大了,那么您可以将BLOCKING_FACTOR除以10或100,但是这样更难以以百分比的形式打印漂亮的输出。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}
}
首先,bar不是唯一的管道进度条。另一个(可能更为人所知)是pv(pipe viewer)。
其次,可以像下面这样使用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 命令进行操作,比如复制 file1 到 file2,那么一个有用的技巧是使用 进程替换:
$ copy <(bar file1) file2
$ copy <(pv file1) file2
bar --in-file /dev/rmt/1cbn --out-file \
tape-restore.tar --size 2.4g --buffer-size 64k
但是在我看来,进程替代是更通用的方法。它使用cp程序本身。
pv
。例如:ssh remote "cd /home/user/ && tar czf - accounts" | pv -s 23091k | tar xz
。 - bitsoflogic