当在bash或*NIX中的任何其他shell中编写脚本时,在运行需要超过几秒钟时间的命令时,需要一个进度条。
例如,复制一个大文件,打开一个大tar文件。
你建议用什么方法向shell脚本添加进度条?
当在bash或*NIX中的任何其他shell中编写脚本时,在运行需要超过几秒钟时间的命令时,需要一个进度条。
例如,复制一个大文件,打开一个大tar文件。
你建议用什么方法向shell脚本添加进度条?
当前回答
我使用了一个答案,从创建重复字符字符串在shell脚本字符重复。我有两个相对较小的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 "#"
这意味着为bar准备50个“#”字符的字符串,在那之后:
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上的进度条,只需在每个printf命令的末尾添加>&2。
其他回答
一些帖子已经展示了如何显示命令的进度。为了计算它,你需要看看你已经进步了多少。在BSD系统上,一些命令,如dd(1),接受SIGINFO信号,并报告它们的进程。在Linux系统上,一些命令的响应类似于SIGUSR1。如果这个工具可用,您可以通过dd管道输入以监视处理的字节数。
或者,您可以使用lsof来获取文件读指针的偏移量,从而计算进度。我编写了一个名为pmonitor的命令,用于显示处理指定进程或文件的进度。有了它,你可以做以下事情。
$ pmonitor -c gzip
/home/dds/data/mysql-2015-04-01.sql.gz 58.06%
Linux和FreeBSD shell脚本的早期版本出现在我的博客上(“在Unix上监视进程进度”)。
要指示活动的进度,请尝试以下命令:
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;
OR
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;
OR
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;
OR
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循环中使用标志/变量来检查和显示进度的值/范围。
首先将进程执行到后台,然后经常观察它的运行状态,即运行时打印模式并再次检查它的状态是否运行;
使用while循环频繁地监视进程的状态。
使用pgrep或任何其他命令来监视和获取进程的运行状态。
如果使用pgrep,则根据需要将不必要的输出重定向到/dev/null。
代码:
sleep 12&
while pgrep sleep &> /dev/null;do echo -en "#";sleep 0.5;done
此“#”将打印直到睡眠终止,此方法用于实现进度条的进度时间程序。
你也可以用这种方法来命令shell脚本,以可视化的方式分析它的进程时间。
错误: 这个pgrep方法在所有情况下都不起作用,出乎意料的是,另一个进程正在以相同的名称运行,while循环没有结束。
通过指定PID来获取进程的运行状态,使用 可能这个过程可以使用一些命令,
命令ps a将列出所有带有id的进程,您需要grep来查找指定进程的pid
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}"
}
我需要一个进度条,将适合弹出气泡消息(通知-发送),以代表电视音量水平。最近我一直在用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 \
卷将弹出气泡消息分组在一起,该组中的新消息立即替换前一个消息。你可以用任何东西来代替体积。