如果使用 |& 而不是 | (管道),则重定向会失败 - bash 错误?

如果使用 |& 而不是 | (管道),则重定向会失败 - bash 错误?

这是一个人工最小化的例子,成功丢弃stdoutstderr通过重定向到下一个命令stdout

strace echo something 2>&1 >/dev/null | wc -l
38

将管道操作符更改为 include-stderr-version|&意外失败,我怀疑是因为调用中的重定向操作顺序错误bash

strace echo something 2>&1 >/dev/null |& wc -l
0

解决方法是使用子 shell:

(strace echo something 2>&1 >/dev/null) |& wc -l
38

这是一个 bash 错误还是一些有用的 bash 功能?

我的期望是,stdout首先设置并连接管道,如果|&使用操作符,则dup连接到stderr第二个,然后最后将命令的“内部”重定向应用于“外部”管道。

虽然man bash明确指出“在命令指定的任何重定向之后,将标准错误隐式重定向到标准输出”,但这似乎是错误的,因为它与之前句子记录的内容不同:“如果|&使用,则命令的标准错误除了其标准输出外,还通过管道连接到命令 2 的标准输入”,事实上,这只有在没有任何进一步重定向的情况下才会发生。

答案1

总结

不是一个错误。|&就像 的别名2>&1 |、简写、减少输入的方法。注意2>&1现在是管道这一部分的最终重定向。就这样。


分析

下面的引用对我来说很清楚。你需要读完整个引用。在问题中,你引用了几句话,但没有更广泛的背景。

管道中每个命令的输出都通过管道连接到下一个命令的输入。也就是说,每个命令都会读取前一个命令的输出。此连接在命令指定的任何重定向之前执行。

如果|&使用,command1的标准错误除了它的标准输出之外,还通过管道连接到command2的标准输入;这是简写2>&1 |。在命令指定的任何重定向之后,都会执行将标准错误隐式重定向到标准输出的操作。

(重点是我的,源链接

以 为例foo … |& bar,其中表示由命令指定的重定向。|&是 的简写(认为:“别名”)2>&1 |,因此命令类似于foo … 2>&1 | bar;而foo部分类似于

foo >fifo … 2>&1

其中>fifo表示与 stdin 的连接bar(我使用了名称fifo,但实际上它是一个匿名管道)。

没有更深层次的哲学,|&只是“扩展”到2>&1 |。然后|表现得像>fifo (“此连接在命令指定的任何重定向之前执行”)并2>&1保持在最后(“在命令指定的任何重定向之后,将标准错误隐式重定向到标准输出”)。一般来说,重定向是从左到右执行的,因此这 >fifo … 2>&1很好地表示了所发生的情况。


例如:你的命令

您的命令:

strace echo something 2>&1 >/dev/null |& wc -l

“扩展” 为:

strace echo something 2>&1 >/dev/null 2>&1 | wc -l

并且该strace部分的行为如下:

strace echo something >fifo 2>&1 >/dev/null 2>&1

where>/dev/null 2>&1将 stdout 重定向到/dev/null,然后将 stderr 重定向到 ,因此也将 重定向到/dev/null。 stdout 和 stderr 在 之前指向哪里根本不重要>/dev/null 2>&1;此特定代码段不依赖于先前的重定向。


示例:您的解决方法

你的解决方法是:

(strace echo something 2>&1 >/dev/null) |& wc -l

其“扩展”为:

(strace echo something 2>&1 >/dev/null) 2>&1 | wc -l

第一部分如下:

(strace echo something 2>&1 >/dev/null) >fifo 2>&1

>fifo 2>&1 >/dev/null 2>&1请注意,这与我们在据称失败的案例中观察到的情况不同。

为了完整性,让我们分析一下(foo 2>&1 >/dev/null) >fifo 2>&1它的工作原理:

  1. 首先创建一个子 shell,它的标准输入被重定向到 fifo(>fifo),它的标准错误被重定向到当前标准输出(最后一个2>&1),所以也重定向到 fifo。

  2. 然后foo将在子 shell 内运行。如果没有进一步的重定向,它将从子 shell 继承标准描述符。这意味着foo两者的“默认”stdout 和 stderr 都将转到 fifo。

  3. 的 Stderrfoo被重定向到其当前 stdout(第一个2>&1)。它们都是 fifo,因此这不会改变任何东西。然后 的 stdoutfoo被重定向到/dev/null( >/dev/null)。

实际上,stderr(但不是 stdout)foo进入 fifo,即通过管道。


结论

整个问题不是“重定向失败”,也不是错误。只是因为|&变成了2>&1 |,但随后又|起作用2>&1行为您指定的任何明确重定向。

不要认为|&|具有额外的功能”。使用它时,请考虑一个别名,一个文本替换:想象一下|&将扩展为2>&1 |(就像你打字 2>&1 |而不是|&);然后2>&1|将独立地完成各自的工作(好像|&从来没有发生过一样)。

相关内容