【问题标题】: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运行使用 exec 和 tee 将标准输出输出发送到终端和文件的脚本后,shell 提示似乎不会重新出现
【发布时间】:2014-01-24 09:42:21
【问题描述】:

我有一个将所有输出写入日志文件的 shell 脚本 和终端,这部分工作正常,但如果我执行脚本 仅当我按 Enter 时才会出现新的 shell 提示。为什么会这样,我该如何解决?

#!/bin/bash

exec > >(tee logfile)
echo "output"

【问题讨论】:

  • 我不知道细节(因此有评论但没有答案),但我怀疑因为tee 和shell 都在异步写入终端,tee 的输出是“覆盖”脚本退出后shell编写的shell提示符。

标签: linux bash shell


【解决方案1】:

首先,当我测试这个时,总是有一个新的shell提示符,只是有时字符串output在它之后,所以提示符不是最后的。你有没有忽略它?如果是这样,似乎有一场比赛,shell 在后台的 tee 完成之前打印提示。

不幸的是,waiting 在 tee 的 shell 中无法解决此问题,请参阅 unix.stackexchange 上的 this question。除了脆弱的解决方法,我看到的解决这个问题的最简单方法是将整个脚本放在一个列表中:

{
your-code-here
} | tee logfile

【讨论】:

  • +1 用于简洁的解决方法。您说得对,问题只是 化妆品 问题 - 但是仍然让用户感到不安。
【解决方案2】:

如果我运行以下脚本(抑制 echo 中的换行符),我会看到提示,但看不到“输出”。字符串仍然写入文件。

#!/bin/bash

exec > >(tee logfile)
echo -n "output"

我怀疑是这样的:您有三个不同的文件描述符试图写入同一个文件(即终端):shell 的标准输出、shell 的标准错误和tee 的标准输出. shell 同步写入:首先将echo 写入标准输出,然后提示标准错误,因此终端能够正确排序它们。但是,第三个文件描述符是由tee 异步写入的,因此存在竞争条件。我不太明白我的修改如何影响比赛,但它似乎破坏了一些平衡,允许在不同的时间编写提示并出现在屏幕上。 (我希望输出缓冲在其中发挥作用)。

您也可以在运行script 命令后尝试运行脚本,该命令将记录写入终端的所有内容;如果您浏览文件中的所有控制字符,您可能会注意到文件中的提示就在tee 编写的输出之前。为了支持我的竞争条件理论,我会注意到在运行脚本几次后,它不再显示“异常”行为;我的 shell 提示符在字符串“输出”之后按预期显示,因此这种情况肯定存在一些不确定因素。

【讨论】:

  • +1 用于分析;请参阅我的答案以获取解决方法。
【解决方案3】:

@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 输出。

【讨论】:

    【解决方案4】:

    正如其他人所指出的,并不是没有打印提示——而是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"
    

    【讨论】:

      猜你喜欢
      • 2018-10-11
      • 2022-09-23
      • 1970-01-01
      • 2020-12-15
      • 2017-12-01
      • 2022-01-15
      • 2016-03-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多