在 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
。
答案1
使用test -t 1
或test -t 2
测试 stdout 或 stderr 是否分别与终端相关联。
-t file_descriptor
如果文件描述符 file_descriptor 已打开并且与终端关联,则为 True。如果文件描述符file_descriptor
不是有效的文件描述符,或者文件描述符file_descriptor
未打开,或者已打开但未与终端关联,则为 False。
(来源)
概念证明:
sh -c '
test -t 1 && echo foo
echo bar
'
按原样运行上述代码,您将看到foo
和bar
。将输出从终端重定向(例如重定向到常规文件或通过管道重定向到cat
)并且foo
不会被打印。
为了避免每次需要打印中间消息时进行测试,请使用以下技巧:
if test -t 1; then exec 5>&1; else exec 5>/dev/null; fi
现在将每个中间消息打印到文件描述符 5(例如echo baz >&5
)。如果是终端,它将转到 stdout,否则转到/dev/null
。