在 bash 中,如果我想生成定期状态消息,我可以通过发出热键组合来替换前一个状态消息,例如,如此处、此处或此处所示。当用户直接执行脚本时,这很好,因为它可以让他们随时了解情况,而不会弄乱屏幕。
但是,如果他们想在自动化上下文中使用相同的脚本,则输出将(必须)重定向到文件中:
./test.sh >log.txt
此时,\r
、 、\b
都\033[0K
不起作用。
我如何使其适用于两种模式?
(例如,如果我可以检测输出流是否是文件,那么省略中间消息就足够简单了。)
- 如果可能的话,我想避免在脚本中引入参数。现实世界的版本已经附带了几个。
- 如果可能的话,我想避免处理脚本本身内部的输出流,例如根据这个问题,因此当嵌入到其他脚本中时它不会表现出令人惊讶的行为。
示例代码test.sh
:
#!/bin/bash
PREVIOUS_JOB_STATUS=""
SECONDS=0 # auto-incremented by Bash; divide by INTERVAL_SECONDS to get number of retries
INTERVAL_SECONDS=3 # interval between polls to the server
TIMEOUT_SECONDS=10 # how long until we give up, because the server is clearly busy (or dead)
while true; do
RETRY_SECONDS=$SECONDS
if [ $RETRY_SECONDS -lt $INTERVAL_SECONDS ]; then # in the real world, here would be the polling operation
JOB_STATUS='queued'
else
JOB_STATUS='processing'
fi
RESULT=1 # in the real world, here would be ? of the polling operation
if [ $RESULT -eq 0 ]; then # success
exit $RESULT
elif [ $RETRY_SECONDS -gt $TIMEOUT_SECONDS ]; then # failure (or at least surrender)
echo "Timeout exceeded waiting for job to finish. Current Job Status is '$JOB_STATUS'. Current result code is '$RESULT'."
exit $RESULT
elif [ "$PREVIOUS_JOB_STATUS" = "$JOB_STATUS" ]; then # no change yet, replace last status message and try again
echo -en '\033[1A\r\033[K' # move one line up : \033[1A ,
# move to pos1 : \r ,
# delete everything between cursor and end-of-line: \033[K ,
# omit line break : -n
else # job status changed, write new status message and try again
PREVIOUS_JOB_STATUS=$JOB_STATUS
fi
SLEEP_MESSAGE='Waiting for job to finish. Waited '`printf "%02g" $SECONDS`" s. Current job status is '$JOB_STATUS'. Current result code is '$RESULT'."
echo $SLEEP_MESSAGE
sleep $INTERVAL_SECONDS
done
最终输出./test.sh
:
Waiting for job to finish. Waited 00 s. Current job status is 'queued'. Current result code is '1'.
Waiting for job to finish. Waited 09 s. Current job status is 'processing'. Current result code is '1'.
Timeout exceeded waiting for job to finish. Current Job Status is 'processing'. Current result code is '1'.
最终输出./test.sh >log.txt 2>&1
:
Waiting for job to finish. Waited 00 s. Current job status is 'queued'. Current result code is '1'.
Waiting for job to finish. Waited 03 s. Current job status is 'processing'. Current result code is '1'.
^[[1A^M^[[KWaiting for job to finish. Waited 06 s. Current job status is 'processing'. Current result code is '1'.
^[[1A^M^[[KWaiting for job to finish. Waited 09 s. Current job status is 'processing'. Current result code is '1'.
Timeout exceeded waiting for job to finish. Current Job Status is 'processing'. Current result code is '1'.
期望的最终输出./test.sh >log.txt 2>&1
: 等于 的最终输出./test.sh
。
使用
test -t 1
或test -t 2
分别测试 stdout 或 stderr 是否与终端关联。(来源)
概念证明:
按原样运行上面的代码,您将看到
foo
和bar
。将输出重定向到远离终端的位置(例如重定向到常规文件,或通过管道传输到cat
)并且foo
不会被打印。为了避免每次需要打印中间消息时进行测试,请使用以下技巧:
现在将每条中间消息打印到文件描述符 5 (例如
echo baz >&5
)。如果它是终端,它将转到标准输出,/dev/null
否则。