- 当通过管道
tee
将其 stdout 发送到 no-op (:
) 命令时,则不会打印任何内容,并且文件大小为零。 - 当通过管道
tee
将其标准输出发送到 a 时cat
,所有内容都会正确打印,并且文件大小大于零。
这是一个显示它的代码示例(以脚本的第一个输入参数为条件):
#!/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
希望得到其背后的解释。
我对 no-op:
命令的期望是它不应该阻止tee
将输出发送到文件。
答案1
in仍然是一个进程:
,tee ... | :
持有由 shell 设置的管道的读取端,其另一端正tee
在写入。只是:
立即退出,这会阻止它从管道中读取数据。 (为了使管道同时执行操作,shell 必须为管道的每个部分生成一个新进程,即使它只是为了处理 no-op :
。在您的示例中,该进程将运行if
最后一个中的语句管道的一部分,然后在“运行”:
内置函数后最终退出。)
通常的行为是,当管道的读取器退出(读取端文件描述符关闭)时,写入器在下一次写入时收到 SIGPIPE 信号,这会导致其退出。
这通常是您想要的,因为这意味着如果管道的右侧退出,左侧也会退出,并且不会无用地继续可能很长的任务。或者(更糟糕的是)无助地试图写入阻塞的管道,该管道不允许任何写入,因为数据无处可去。
因为,tee
看起来没有任何例外POSIX 规范;最接近的部分是提到文件操作数的写入错误:
如果对任何成功打开的文件操作数的写入失败,则对其他成功打开的文件操作数和标准输出的写入应继续,但退出状态应为非零。
如果 SIGPIPE 被忽略,我测试的实现将继续执行EPIPE
然后从调用返回的错误write()
。
GNU coreutils 版本tee
具有-p
和--output-error
选项来控制写入失败时执行的操作:
未指定时的默认操作
--output-error
是在写入管道时发生错误时立即退出,并诊断写入非管道输出的错误。
虽然它退出的方式是通过 SIGPIPE,所以tee
从忽略信号开始,它不会退出。
默认的-p
模式是warn-nopipe
“诊断写入任何非管道输出的错误”,而不是其他使其退出的选项。在幕后,它还会忽略 SIGPIPE 信号,然后停止尝试写入管道。
因此,至少对于 GNU 版本,您可以使用tee -p ... | ...
它来防止管道读取器退出时退出。或者,您可以将右侧程序安排为模仿黑洞,例如cat > /dev/null
(仍然显示和写入它得到的所有内容,但内核最终会忽略写入的数据/dev/null
)。
答案2
这:
不是一个 nop 进程。这并非cat
毫无争议。不读取 stdin,并传递到 stdout(因为这不会是 nop。我看到它可以被视为 nop 管道阶段,但这是另一个想法)。这不是一个过程。
您正试图通过管道传递任何内容。我不知道外壳是做什么的,但如果管道没有连接到任何东西,我也不会感到惊讶。 (或者可能附加到外壳,并被忽略)。无论如何,我不希望发生任何好事。
正如@Kusalananda 所说。管道将等待其输出处的进程读取[或至少关闭管道],但没有进程读取或关闭管道。您可以通过管道连接到cat >/dev/null
[(多态)。或者,如果您可以重定向 > /dev/null
(静态)]。
其中之一(如果可能的话最好不要有猫)
| cat >/dev/null
>/dev/null
另一种多态解决方案(避免面包屑多余的猫):
if ...
then
out=/dev/stdout
else
out=/dev/null
fi
command >"$out"
答案3
由于 a:
永远不会从 stdin 读取,因此它会产生 PIPE 信号。收到 PIPE 信号后,tee(默认情况下)退出。使用a:
将会停止一切。
--output-error=warn
如果使用 GNU tee(我假设您使用 bash 后已经有了)(不是 tee POSIX 选项),您可以通过添加到 tee 来看到这一点。
您可以使用以下方法进行测试:
$ echo "hello" | tee --output-error=warn | :
tee: 'standard output': Broken pipe
更改:
为cat >/dev/null
将避免 PIPE 导致tee
退出。
但:为什么使用 shell 循环处理文本被认为是不好的做法?
逐行读取文本文件(此处的文档)然后将其发送到管道并不是最好的选择。
考虑:
sed 's/^/preffix: /' <<'EOF' | tee -a "$log_filepath"
1
23
456
7890
EOF
您还可以根据变量的值在数组内设置 tee 选项。
并且还将输出设置在变量而不是此处文档中:
#!/bin/bash --
log_filepath="./log.txt"
[ -f "$log_filepath" ] && { rm "$log_filepath" || exit 1 ; }
enable_stdout="${1:+"yes"}"
enable_log="yes" # comment this line to avoid logging.
out=$'1\n23\n456\n7890\n'
unset teeoptions; teeoptions=()
[ -n "$enable_stdout" ] && teeoptions+=(-a "/dev/tty")
[ -n "$enable_log" ] && teeoptions+=(-a "$log_filepath")
printf '%s' "$out" |
sed 's/^/preffix: /' |
tee "${teeoptions[@]}" >/dev/null