这是一个人工最小化的例子,成功丢弃stdout
并stderr
通过重定向到下一个命令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
它的工作原理:
首先创建一个子 shell,它的标准输入被重定向到 fifo(
>fifo
),它的标准错误被重定向到当前标准输出(最后一个2>&1
),所以也重定向到 fifo。然后
foo
将在子 shell 内运行。如果没有进一步的重定向,它将从子 shell 继承标准描述符。这意味着foo
两者的“默认”stdout 和 stderr 都将转到 fifo。的 Stderr
foo
被重定向到其当前 stdout(第一个2>&1
)。它们都是 fifo,因此这不会改变任何东西。然后 的 stdoutfoo
被重定向到/dev/null
(>/dev/null
)。
实际上,stderr(但不是 stdout)foo
进入 fifo,即通过管道。
结论
整个问题不是“重定向失败”,也不是错误。只是因为|&
变成了2>&1 |
,但随后又|
起作用前和2>&1
行为后您指定的任何明确重定向。
不要认为|&
“|
具有额外的功能”。使用它时,请考虑一个别名,一个文本替换:想象一下|&
将扩展为2>&1 |
(就像你打字 2>&1 |
而不是|&
);然后2>&1
和|
将独立地完成各自的工作(好像|&
从来没有发生过一样)。