我有一个shell脚本,它将所有输出写入logfile 和终端,这部分工作正常,但如果我执行脚本 只有按Enter键才会出现新的shell提示符。为什么这样,我该如何解决?
#!/bin/bash
exec > >(tee logfile)
echo "output"
答案 0 :(得分:2)
首先,当我测试这个时,总是 一个新的shell提示符,只是有时字符串output
在它之后,所以提示不是最后一个。你碰巧忽略了吗?如果是这样,那么shell似乎会在后台tee
完成之前打印提示。
不幸的是,wait
的shell tee
无法解决这个问题,请参阅unix.stackexchange上的this question。除了脆弱的解决方法之外,我看到解决这个问题的最简单方法是将整个脚本放在列表中:
{
your-code-here
} | tee logfile
答案 1 :(得分:1)
如果我运行以下脚本(从echo
抑制换行),我会看到提示,但不是“输出”。该字符串仍然写入文件。
#!/bin/bash
exec > >(tee logfile)
echo -n "output"
我怀疑是这样的:你有三个不同的文件描述符试图写入同一个文件(即终端):shell的标准输出,shell的标准错误,以及{{1的标准输出}}。 shell同步写入:首先是tee
到标准输出,然后是标准错误的提示,因此终端能够正确地对它们进行排序。但是,第三个文件描述符由echo
异步写入,因此存在竞争条件。我不太明白我的修改如何影响比赛,但它似乎打乱了一些平衡,允许提示在不同的时间写入并出现在屏幕上。 (我希望输出缓冲能够发挥作用)。
您也可以在运行tee
命令后尝试运行脚本,该命令将记录写入终端的所有内容;如果你浏览文件中的所有控制字符,你可能会在script
写的输出之前就注意到文件中的提示。为了支持我的竞争条件理论,我会注意到在运行脚本几次后,它不再显示“异常”行为;我的shell提示符在字符串“output”之后按预期显示,因此这种情况肯定存在一些非确定性元素。
答案 2 :(得分:0)
@ chepner的回答提供了很好的背景信息。
这是一个解决方法 - 适用于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 logfile
。
正如@chepner已经暗示的那样,外部解决方案是使用script
实用程序创建脚本输出的“脚本”以及显示它:
script -qc yourScript /dev/null > logfile # Linux syntax
然而,这也将捕获 stderr 输出;如果你想避免这种情况,请使用:
script -qc 'yourScript 2>/dev/null' /dev/null > logfile
但请注意,这将完全抑制stderr输出。
答案 3 :(得分:0)
正如其他人所指出的,并不是没有提示打印出来,而是tee
编写的输出的最后一个可以在提示之后 出现,从而使该提示不再可见
如果您使用的bash 4.4或更高版本,则可以wait
退出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"