我有一个shell脚本,将所有输出写入logfile和终端,此部分正常工作,但是如果我执行脚本仅当我按Enter时出现新的外壳提示。为什么那是这样,我该如何修复?
#!/bin/bash
exec > >(tee logfile)
echo "output"
首先,当我对此进行测试时,总有一个新的shell提示最后。您碰巧忽略了它吗?如果是这样,似乎有一场比赛,外壳在后台的tee
之前打印了提示。
不幸的是,无法通过在shell中的wait
来解决tee
的CC_3,请在unix.stackexchange上参见此问题。除了脆弱的解决方案,解决此问题的最简单方法是将您的整个脚本放入列表中:
{
your-code-here
} | tee logfile
如果我运行以下脚本(从 echo
抑制新线),我会看到提示,但没有"输出"。字符串仍写入文件。
#!/bin/bash
exec > >(tee logfile)
echo -n "output"
我怀疑的是:您有三个不同的文件描述符,试图写入同一文件(即终端):壳的标准输出,外壳的标准误差和tee
的标准输出。外壳同步写入:首先将echo
到标准输出,然后提示到标准误差,因此终端能够正确地对其进行排序。但是,第三个文件描述符由tee
编写为异步,因此存在种族条件。我不太了解我的修改如何影响比赛,但是它似乎使某些平衡失望,可以在不同的时间写下提示并出现在屏幕上。(我希望输出缓冲在此中发挥作用)。
运行script
命令后,您还可以尝试运行脚本,该命令将记录所有写入终端的所有内容;如果您浏览文件中的所有控制字符,则可能会在output
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输出。
正如其他人所指出的,并不是没有任何提示 - 这是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"