创建 Bash 旋转器
发布于 2009 年 6 月 9 日
您知道字符序列 "/-\|/-\|" 是做什么用的吗?这是一个基于文本的旋转器。仍然感到困惑,请继续阅读。
您可能已经见过一些长时间运行的控制台程序,它们在运行时会显示一个旋转器。旋转器就是前面提到的字符序列,一个接一个地在屏幕上的同一位置输出,并在每个字符之间暂停。本文中的代码创建了一个在单独进程中运行的旋转器。通过这样做,旋转器以恒定的速率旋转,并且在您的脚本暂停时不会暂停。它还消除了在程序中散布旋转器输出消息的需要。
此外,旋转器从主进程创建的日志文件中读取内容,并在旋转器旁边显示日志文件的最后一行。当日志文件消失时,旋转器退出。
这是主进程的脚本,名为 runner.sh
#!/bin/bash
logfile=/tmp/mylog
echo >$logfile
trap "rm -f $logfile" EXIT
# Output message to log file.
function log_msg()
{
echo "$*" >>$logfile
}
# Start spinner
sh spinner.sh &
# Perform really long task.
i=0
log_msg "Starting a really long job"
while [[ $i -lt 100 ]]
do
sleep 1
let i+=5
log_msg "$i% complete"
done
sleep 1
echo
顶部的函数用于将消息输出到日志文件。日志文件在程序的顶部初始化为空文件,输出函数将其附加到该文件。
在进入其主循环之前,主进程在后台启动旋转器。之后,它只是暂停一下,输出一条状态消息,然后重复直到它 100% 完成。
以下是旋转器进程
#!/bin/bash
logfile=/tmp/mylog
logsize=0
spinpause=0.10
linelen=0
# Output last line from log file.
function lastout()
{
local line=$(tail -n 1 $logfile 2>/dev/null)
if [[ "$line" ]]; then
echo -n " $line"
# Erase any extra from last line.
local len
let len=$linelen-${#line}
while [[ $len -gt 0 ]]
do
echo -n " "
let len--
done
linelen=${#line}
fi
}
# Output a spin character.
function spinout()
{
local spinchar="$1"
local sz
local ll
if [[ -f $logfile ]]; then
echo -n -e "\r$spinchar"
sleep $spinpause
# Check for new message.
sz=$(stat --printf '%s' $logfile 2>/dev/null)
if [[ $sz -gt $logsize ]]; then
lastout
logsize=$sz
fi
fi
}
if [[ -f $logfile ]]; then
logsize=$(stat --printf '%s' $logfile 2>/dev/null)
if [[ $logsize -gt 0 ]]; then
echo -n " "
lastout
fi
while [[ -f $logfile ]]
do
spinout "/"
spinout "-"
spinout "\\"
spinout "|"
spinout "/"
spinout "-"
spinout "\\"
spinout "|"
done
echo
fi
旋转器包含两个函数。第一个函数输出日志文件的最后一行。由于日志文件行始终放置在同一屏幕行上,因此新的日志行可能比上一条日志行短。因此,在输出新行后,该函数会输出空格,直到达到前一条日志行的长度,以便将其完全擦除。
第二个函数输出一个旋转器字符,并暂停字符间的暂停时间。它在旋转器字符之前添加一个回车符,以便所有内容都保留在同一行上。输出旋转器字符后,它会检查自上次输出行以来日志文件的大小是否增加。如果增加了,它会输出新的一行。
旋转器的主循环一个接一个地输出每个旋转器字符。
观看随附的视频以查看其运行情况。