Bash:“tee”被下一个管道中的“no-op”阻止

Dor*_*Dor 4 bash pipe

  • 当通过管道tee将其 stdout 发送到 no-op ( :) 命令时,则不会打印任何内容,并且文件大小为零。
  • 当通过管道tee将其标准输出发送到 acat时,所有内容都会正确打印,并且文件大小大于零。

这是一个显示它的代码示例(以脚本的第一个输入参数为条件):

#!/usr/bin/env bash

log_filepath="./log.txt"
[ -f "$log_filepath" ] && { rm "$log_filepath" || exit 1 ; }

fail_tee="$1"

while IFS= read -r -d $'\n' line ; do
    printf "%s%s\n" "prefix: " "$line"  | \
    tee -a "$log_filepath"              | \
    {
        if [ -n "$fail_tee" ]; then
            # Nothing is printed to stdout/terminal
            # $log_filepath size is ZERO.
            : # do nothing. 
        else
            # Each line in the input is prefixed w/ "prefix: " and sent to stdout
            # $log_filepath size is 46 bytes
            cat
        fi
    }
done <<'EOF'
1
23
456
7890
EOF
Run Code Online (Sandbox Code Playgroud)

希望得到其背后的解释。
我对 no-op:命令的期望是它不应该阻止tee将输出发送到文件。

ilk*_*chu 5

in仍然:tee ... |\xc2\xa0:一个进程,持有由 shell 设置的管道的读取端,其另一端正tee在写入。只是:立即退出,这会阻止它从管道中读取数据。(为了使管道同时执行操作,外壳程序必须为管道的每个部分生成一个新进程,即使它只是为了处理 no-op 。:在您的示例中,该进程将运行if语句在管道的最后一部分,然后在“运行”:内置函数后最终退出。)

\n

通常的行为是,当管道的读取器退出(读取端文件描述符关闭)时,写入器在下一次写入时收到 SIGPIPE 信号,这会导致其退出。

\n

这通常是您想要的,因为这意味着如果管道的右侧退出,左侧也会退出,并且不会无用地继续可能很长的任务。或者(更糟糕的是)无助地试图写入阻塞的管道,该管道不允许任何写入,因为数据无处可去。

\n

因为,POSIX 规范tee中似乎没有任何例外;最接近的部分是提到文件操作数的写入错误:

\n
\n

如果对任何成功打开的文件操作数的写入失败,则对其他成功打开的文件操作数和标准输出的写入应继续,但退出状态应为非零。

\n
\n

如果 SIGPIPE 被忽略,我测试的实现将继续执行然后从调用EPIPE返回的错误write()

\n

GNU coreutils 版本tee具有-p--output-error选项来控制写入失败时执行的操作:

\n
\n

未指定时的默认操作--output-error是在写入管道时发生错误时立即退出,并诊断写入非管道输出的错误。

\n
\n

虽然它退出的方式是通过 SIGPIPE,所以tee从忽略信号开始,它不会退出。

\n

默认的-p模式warn-nopipe是“诊断写入任何非管道输出的错误”,而不是其他使其退出的选项。在幕后,它还会忽略 SIGPIPE 信号,然后停止尝试写入管道。

\n

因此,至少对于 GNU 版本,您可以使用tee -p ... |\xc2\xa0...它来防止管道读取器退出时退出。或者,您可以将右侧程序安排为模仿黑洞,例如cat > /dev/null(它仍然读取写入它获得的所有内容,但内核最终会忽略写入的数据/dev/null)。

\n