[英]How does/frequent unix tee command write stdout terminal output to file? if the output is too big
[英]shell prompt seemingly does not reappear after running a script that uses exec with tee to send stdout output to both the terminal and a file
我有一个 shell 脚本,它将所有输出写入日志文件和终端,这部分工作正常,但是如果我执行脚本,只有按 Enter 时才会出现新的 shell 提示。 为什么会这样,我该如何解决?
#!/bin/bash
exec > >(tee logfile)
echo "output"
首先,当我测试这个的时候,总是有一个新的 shell 提示,只是有时字符串output
在它后面,所以提示不是最后一个。 是不是你忽略了? 如果是这样,似乎存在一种竞争,即 shell 在后台的tee
完成之前打印提示。
不幸的是,这不能通过在 shell 中wait
tee
,请参阅 unix.stackexchange 上的这个问题。 撇开脆弱的解决方法不谈,我认为解决这个问题的最简单方法是将整个脚本放在一个列表中:
{
your-code-here
} | tee logfile
如果我运行以下脚本(从echo
抑制换行符),我会看到提示,但看不到“输出”。 该字符串仍会写入文件。
#!/bin/bash
exec > >(tee logfile)
echo -n "output"
我怀疑的是:您有三个不同的文件描述符试图写入同一个文件(即终端):shell 的标准输出、shell 的标准错误和tee
的标准输出。 shell 同步写入:首先将echo
发送到标准输出,然后提示标准错误,因此终端能够正确地对它们进行排序。 但是,第三个文件描述符由tee
异步写入,因此存在竞争条件。 我不太明白我的修改如何影响比赛,但它似乎打破了一些平衡,允许在不同的时间写入提示并出现在屏幕上。 (我希望输出缓冲在其中发挥作用)。
您也可以尝试在运行script
命令后运行您的script
,这将记录写入终端的所有内容; 如果您遍历文件中的所有控制字符,您可能会注意到文件中的提示就在tee
写入的输出之前。 为了支持我的竞争条件理论,我会注意到在运行脚本几次后,它不再显示“异常”行为; 我的 shell 提示在字符串“输出”之后按预期显示,因此这种情况肯定存在一些不确定因素。
@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"
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.