在运行使用带有TEE的exec将stdout输出发送到终端和文件后,Shell提示似乎不会重新出现



我有一个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命令后,您还可以尝试运行脚本,该命令将记录所有写入终端的所有内容;如果您浏览文件中的所有控制字符,则可能会在output0编写的输出之前注意到文件中的提示。为了支持我的种族条件理论,我会注意到几次运行脚本后,它不再显示"异常"行为。在字符串"输出"之后,我的外壳提示如预期显示,因此这种情况肯定有一些不确定性的元素。

@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"

最新更新