将stderr与stdout结合使用时,为什么2>&1
需要在|
(管道)之前,但在> myfile
(重定向到文件)之后?为什么2>&1需要在|之前出现(管道),但后“> myfile”(重定向到文件)?
重定向错误输出到标准输出文件输出:
echo > myfile 2>&1
重定向错误输出到标准输出的管道:
echo 2>&1 | less
我的假设是,我可以这样做:
echo | less 2>&1
它很害怕d工作,但事实并非如此。为什么不?
将stderr与stdout结合使用时,为什么2>&1
需要在|
(管道)之前,但在> myfile
(重定向到文件)之后?为什么2>&1需要在|之前出现(管道),但后“> myfile”(重定向到文件)?
重定向错误输出到标准输出文件输出:
echo > myfile 2>&1
重定向错误输出到标准输出的管道:
echo 2>&1 | less
我的假设是,我可以这样做:
echo | less 2>&1
它很害怕d工作,但事实并非如此。为什么不?
A 管道是命令的限制列表。您指定的任何重定向适用于组成命令(简单或复合),但不适用于整个管道。每个管道将一个命令的stdout链接到下一个stdin,方法是隐式地将重定向应用于每个子shell ,然后对与命令相关联的任何重定向进行评估。
cmd 2>&1 | less
第一子外壳的第一标准输出被重定向到从中less
正在读取管。接下来,将2>&1
重定向应用于第一个命令。将标准错误重定向到标准输出是可行的,因为标准输出已经指向管道。
cmd | less 2>&1
这里,重定向适用于less
。较少的stdout和stderr都可能开始指向终端,因此在这种情况下2>&1
不起作用。
如果要重定向到适用于整个管道,将多个命令作为管道的一部分,或者嵌套管道,然后使用一个命令组(或任何其它化合物命令):
{ { cmd1 >&3; cmd2; } 2>&1 | cmd3; } 3>&2
可能是一个典型的例子。最终结果是:cmd1
和cmd2
的stderr - >cmd3
; cmd2
的标准输出 - >cmd3
;和cmd1
和cmd3
的stderr,和cmd3
的标准输出 - >终端。
如果使用特定于Bash的|&
管道,事情会变得很陌生,因为每个管道的stdout重定向仍然首先发生,但stderr重定向实际上是最后一次。例如:
f() { echo out; echo err >&2; }; f >/dev/null |& cat
现在,违反直觉,所有的输出都是隐藏的。 f
的第一个stdout进入管道,f
的下一个stdout被重定向到/dev/null
,最后,stderr被重定向到stdout(仍然为/dev/null
)。
我推荐在Bash中从不使用|&
- 这里用于演示。
为了增加ormaaj的回答是:
你需要以适当的顺序来指定重定向操作符的原因是由左到右,他们正在评估。考虑这些命令列表:
# print "hello" on stdout and "world" on stderr
{ echo hello; echo world >&2; }
# Redirect stdout to the file "out"
# Then redirect stderr to the file "err"
{ echo hello; echo world >&2; } > out 2> err
# Redirect stdout to the file "out"
# Then redirect stderr to the (already redirected) stdout
# Result: all output is stored in "out"
{ echo hello; echo world >&2; } > out 2>&1
# Redirect stderr to the current stdout
# Then redirect stdout to the file "out"
# Result: "world" is displayed, and "hello" is stored in "out"
{ echo hello; echo world >&2; } 2>&1 > out
我的答案是通过理解文件描述符。每个进程都有一堆文件描述符:打开文件的条目。默认情况下,编号0用于标准输入,编号1用于标准输出,编号2用于标准错误。
默认情况下,I/O转向器>和<连接到它们最合理的文件描述符stout和stdin。如果将stdout重新路由到文件(如foo > bar
),则在启动进程'foo'时,会打开文件'bar'进行写入并挂接文件描述符编号1.如果只希望在'bar'中使用stderr, '使用foo 2> bar
打开文件栏并将其挂接到stderr。
现在I/O转向器'2> & 1'。我通常读为'将文件描述符2放置为与文件描述符1相同。从左到右阅读命令行时,您可以执行下一个:foo 1>bar 2>&1 1>/dev/tty
。由此,文件描述符1被设置为文件'bar',文件描述符2被设置为与1(因此'bar')相同,并且之后,文件描述符1被设置为/ dev/tty。运行foo
将其输出发送到/ dev/tty,并将其stderr发送到文件“bar”。
现在流水线进来了:这不会改变文件描述符,但是它会在进程之间连接它们:下一个进程的stdin标准输出。斯德尔传递。因此,如果您希望管道仅在stderr上工作,则使用foo 2| bar
,它将foo
的stderr连接到bar
的stdin。 (我不知道用的foo
标准输出会发生什么。)
通过以上,如果使用foo 2>&1 | bar
,因为foo
标准错误重新路由到的foo
标准输出,标准输出和标准错误foo
在到达标准号为bar
。
+1好解释 – jordanm 2012-07-20 00:19:17
+1。我唯一要添加的是管道是一个命令分隔符,就像分号一样。 – 2012-07-20 02:19:55
+1哇,优秀的答案@ormaaj!正是我在找什么 - 谢谢! – 2012-07-20 14:23:37