当在bash或*NIX中的任何其他shell中编写脚本时,在运行需要超过几秒钟时间的命令时,需要一个进度条。
例如,复制一个大文件,打开一个大tar文件。
你建议用什么方法向shell脚本添加进度条?
当在bash或*NIX中的任何其他shell中编写脚本时,在运行需要超过几秒钟时间的命令时,需要一个进度条。
例如,复制一个大文件,打开一个大tar文件。
你建议用什么方法向shell脚本添加进度条?
当前回答
根据上面列出的建议,我决定实现自己的进度条。
#!/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 "$@"
其他回答
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}"
}
大多数unix命令不会为您提供可以执行此操作的直接反馈。 有些将在您可以使用的stdout或stderr上提供输出。
对于tar之类的东西,您可以使用-v开关并将输出管道到一个程序,该程序为读取的每一行更新一个小动画。当tar写出一个文件列表时,程序可以更新动画。要计算完成百分比,您必须知道文件的数量并计算行数。
据我所知,cp没有给出这种输出。要监视cp的进程,必须监视源文件和目标文件,并监视目标文件的大小。你可以写一个小的c程序,使用stat(2)系统调用来获取文件大小。这将读取源文件的大小,然后轮询目标文件,并根据到目前为止写入的文件的大小更新% complete条。
我更喜欢使用dialog和——gauge参数。在许多发行版的.deb包安装和其他基本配置中经常使用。所以你不需要重新发明轮子……再一次
只要输入一个从1到100的int值@stdin。举个简单而愚蠢的例子:
for a in {1..100}; do sleep .1s; echo $a| dialog --gauge "waiting" 7 30; done
我有这个/bin/Wait文件(带有chmod u+x perms)用于烹饪
#!/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"
所以我可以写:
等"34分钟" "预热烤箱"
or
等待“12月31日”“新年快乐”
首先,杆并不是唯一的管道进度仪表。另一个(可能更广为人知)是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 file1 file2,一个有用的技巧是使用进程替换:
$ copy <(bar file1) file2
$ copy <(pv file1) file2
进程替换是bash的一个神奇的东西,它创建临时fifo管道文件/dev/fd/,并通过该管道连接运行进程(括号内)的stdout,复制看到它就像一个普通文件一样(只有一个例外,它只能向前读取)。
更新:
Bar命令本身也允许复制。男子酒吧后:
bar --in-file /dev/rmt/1cbn --out-file \
tape-restore.tar --size 2.4g --buffer-size 64k
但是在我看来,过程替换是更通用的方法。它本身使用cp程序。
要制作一个tar进度条
tar xzvf pippo.tgz |xargs -L 19 |xargs -I@ echo -n "."
其中“19”是tar中的文件数除以预期进度条的长度。 例如:.tgz包含140个文件,你想要一个76 "."的进度条,你可以放-L 2。
你什么都不需要了。