Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/linux/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Linux 运行使用exec with tee将标准输出发送到终端和文件的脚本后,shell提示符似乎不会重新出现_Linux_Bash_Shell - Fatal编程技术网

Linux 运行使用exec with tee将标准输出发送到终端和文件的脚本后,shell提示符似乎不会重新出现

Linux 运行使用exec with tee将标准输出发送到终端和文件的脚本后,shell提示符似乎不会重新出现,linux,bash,shell,Linux,Bash,Shell,我有一个shell脚本,它将所有输出写入日志文件 终端,这部分工作正常,但是如果我执行脚本 仅当我按enter键时,才会出现新的shell提示。为什么会这样?我该如何修复它 #!/bin/bash exec > >(tee logfile) echo "output" 如果我运行以下脚本(从echo中取消换行符),我会看到提示,但不会看到“输出”。字符串仍会写入文件 #!/bin/bash exec > >(tee logfile) echo -n "output"

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

#!/bin/bash

exec > >(tee logfile)
echo "output"

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

#!/bin/bash

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


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

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

不幸的是,在shell中等待
tee
,无法解决此问题,请参阅unix.stackexchange上的。撇开脆弱的变通办法不谈,我认为解决这个问题的最简单方法是将整个脚本放在一个列表中:

{
your-code-here
} | tee logfile

@切普纳的回答提供了大量的背景信息

以下是一个解决方案——适用于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日志文件中

@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或更高版本,您可以
等待
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"

我不知道细节(因此有一条评论,没有回答),但我怀疑,因为
tee
和shell都是异步写入终端的,
tee
的输出是在脚本退出后“覆盖”shell编写的shell提示。+1是一个简单的解决方法。你是对的,这个问题只是一个表面的问题-仍然让用户感到不安,尽管如此。+1用于分析;请参阅我的答案以了解解决方法。