Linux 运行使用exec with tee将标准输出发送到终端和文件的脚本后,shell提示符似乎不会重新出现
我有一个shell脚本,它将所有输出写入日志文件 终端,这部分工作正常,但是如果我执行脚本 仅当我按enter键时,才会出现新的shell提示。为什么会这样?我该如何修复它Linux 运行使用exec with tee将标准输出发送到终端和文件的脚本后,shell提示符似乎不会重新出现,linux,bash,shell,Linux,Bash,Shell,我有一个shell脚本,它将所有输出写入日志文件 终端,这部分工作正常,但是如果我执行脚本 仅当我按enter键时,才会出现新的shell提示。为什么会这样?我该如何修复它 #!/bin/bash exec > >(tee logfile) echo "output" 如果我运行以下脚本(从echo中取消换行符),我会看到提示,但不会看到“输出”。字符串仍会写入文件 #!/bin/bash exec > >(tee logfile) echo -n "output"
#!/bin/bash
exec > >(tee logfile)
echo "output"
如果我运行以下脚本(从
echo
中取消换行符),我会看到提示,但不会看到“输出”。字符串仍会写入文件
#!/bin/bash
exec > >(tee logfile)
echo -n "output"
我怀疑的是:有三个不同的文件描述符试图写入同一个文件(即终端):shell的标准输出、shell的标准错误和tee的标准输出。shell同步写入:首先将echo
写入标准输出,然后提示标准错误,这样终端就能够正确地对它们进行排序。但是,第三个文件描述符由tee
异步写入,因此存在争用条件。我不太明白我的修改是如何影响比赛的,但它似乎打破了一些平衡,允许在不同的时间写入提示并显示在屏幕上。(我希望输出缓冲在其中发挥作用)
您还可以在运行script
命令后尝试运行脚本,该命令将记录写入终端的所有内容;如果浏览文件中的所有控制字符,您可能会注意到文件中的提示,就在tee
写入输出之前。为了支持我的竞争条件理论,我将注意到在运行脚本几次之后,它不再显示“异常”行为;我的shell提示符按预期显示在字符串“output”之后,因此这种情况肯定有一些不确定的元素。首先,当我测试这个时,总是有一个新的shell提示符,只是有时候字符串output
在它之后,所以提示符不是最后一个。你碰巧忽略了吗?如果是这样的话,似乎有一场比赛,shell在后台的tee
完成之前打印提示符
不幸的是,在shell中等待tee
,无法解决此问题,请参阅unix.stackexchange上的。撇开脆弱的变通办法不谈,我认为解决这个问题的最简单方法是将整个脚本放在一个列表中:
{
your-code-here
} | tee logfile
@切普纳的回答提供了大量的背景信息
以下是一个解决方案——适用于Ubuntu 12.04(Linux 3.2.0)和OS X 10.9.1:
#!/bin/bash
exec > >(tee logfile)
echo "output"
# WORKAROUND - place LAST in your script.
# Execute an executable (as opposed to a builtin) that outputs *something*
# to make the prompt reappear normally.
# In this case we use the printf *executable* to output an *empty string*.
# Use of `$ec` is to ensure that the script's actual exit code is passed through.
ec=$?; $(which printf) ''; exit $ec
备选方案:
@user2719058的回答显示了一个简单的替代方案:将整个脚本主体包装在一个组命令({…}
)中,并将其管道化到tee日志文件中
@chepner已经暗示,一个外部解决方案是使用script
实用程序创建脚本输出的“转录本”,并显示它:
script -qc yourScript /dev/null > logfile # Linux syntax
然而,这也将捕获stderr输出;如果要避免这种情况,请使用:
script -qc 'yourScript 2>/dev/null' /dev/null > logfile
但是,请注意,这将完全抑制stderr输出。正如其他人所指出的,并不是没有打印提示,而是由tee
编写的最后一个输出可能出现在提示之后,使提示不再可见
如果您有bash 4.4或更高版本,您可以等待tee
进程退出,如下所示:
#!/usr/bin/env bash
case $BASH_VERSION in ''|[0-3].*|4.[0-3]) echo "ERROR: Bash 4.4+ needed" >&2; exit 1;; esac
exec {orig_stdout}>&1 {orig_stderr}>&2 # make a backup of original stdout
exec > >(tee -a "_install_log"); tee_pid=$! # track PID of tee after starting it
cleanup() { # define a function we'll call during shutdown
retval=$?
exec >&$orig_stdout # Copy your original stdout back to FD 1, overwriting the pipe to tee
exec 2>&$orig_stderr # If something overwrites stderr to also go through tee, fix that too
wait "$tee_pid" # Now, wait until tee exits
exit "$retval" # and complete exit with our original exit status
}
trap cleanup EXIT # configure the function above to be called during cleanup
echo "Writing something to stdout here"
我不知道细节(因此有一条评论,没有回答),但我怀疑,因为tee
和shell都是异步写入终端的,tee
的输出是在脚本退出后“覆盖”shell编写的shell提示。+1是一个简单的解决方法。你是对的,这个问题只是一个表面的问题-仍然让用户感到不安,尽管如此。+1用于分析;请参阅我的答案以了解解决方法。