Note that the order of redirections is significant. For example, the command
ls > dirlist 2>&1
directs both standard output and standard error to the file dirlist,
while the command
ls 2>&1 > dirlist
directs only the standard output to file dirlist, because the
standard error was duplicated from the standard output before the standard
output was redirected to dirlist.
现在,最后一部分让我感到困惑。在这种情况下,任何标准错误都将打印到终端,并且任何 STDOUT 都将转到目录列表文件。这就是会发生的情况,但这不是我对手册的理解。
似乎应该说“因为在标准输出重定向到 dirlist 之后,标准错误是从标准输出复制的”。如果在 STDOUT 定向到文件之前将 STDERR 发送到 STDOUT,那么该文件不会包含 STDOUT 和 STDERR 吗?
有人可以帮我解决这个问题吗?难道只是我阅读理解能力差?在这种情况下,“重复”这个词的使用对我来说似乎有点奇怪。也许这让我很困惑。
答案1
复制这确实是这里的重要部分。
让我们看看文件描述符在重定向之前会去哪里。这通常是当前终端,例如:
STDOUT ---> /dev/pts/1
STDERR ---> /dev/pts/1
现在,如果我们ls -l
在没有重定向的情况下调用,输出和错误消息将转到我的终端下的/dev/pts/1
.
如果我们首先将其重定向STDOUT
到一个文件 ( ls -l > dirlist
),它看起来像这样:
STDOUT ---> /home/bon/dirlist
STDERR ---> /dev/pts/1
什么时候我们然后重定向STDERR
到复制ofSTDOUT
的文件描述符 ( ls -l > dirlist 2>&1
),STDERR
转到 的副本/home/bon/dirlist
:
STDOUT ---> /home/bon/dirlist
STDERR ---> /home/bon/dirlist
如果我们愿意第一的重定向到的文件描述符 ( )STDERR
的副本:STDOUT
ls -l 2>&1
STDOUT ---> /dev/pts/1
STDERR ---> /dev/pts/1
和然后 STDOUT
对于文件 ( ls -l 2>&1 > dirlist
),我们会得到:
STDOUT ---> /home/bon/dirlist
STDERR ---> /dev/pts/1
到这里,STDERR
还是要去终点站。
您看,手册页中的顺序是正确的。
测试重定向
现在,您可以自己测试一下。使用ls -l /proc/$$/fd/
,您可以看到STDOUT
(使用 fd 1)和STDERR
(使用 fd 2)当前进程的走向:
$ ls -l /proc/$$/fd/
total 0
lrwx------ 1 bon bon 64 Jul 24 18:19 0 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 18:19 1 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 07:41 2 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 18:19 255 -> /dev/pts/1
让我们创建一个小的 shell 脚本来显示文件描述符所指向的位置。这样,我们总是可以在调用时获取状态ls
,包括来自调用 shell 的任何重定向。
$ cat > lookfd.sh
#!/bin/sh
ls -l /proc/$$/fd/
^D
$ chmod +x lookfd.sh
(使用CtrlD,您可以发送文件结束符,从而停止cat
从 读取命令STDIN
。)
现在,使用不同的重定向组合调用此脚本:
$ ./lookfd.sh
total 0
lrwx------ 1 bon bon 64 Jul 24 19:08 0 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 19:08 1 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 19:08 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:08 255 -> /home/bon/lookfd.sh
$ ./lookfd.sh > foo.out
$ cat foo.out
total 0
lrwx------ 1 bon bon 64 Jul 24 19:10 0 -> /dev/pts/1
l-wx------ 1 bon bon 64 Jul 24 19:10 1 -> /home/bon/foo.out
lrwx------ 1 bon bon 64 Jul 24 19:10 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:10 255 -> /home/bon/lookfd.sh
$ ./lookfd.sh 2>&1 > foo.out
$ cat foo.out
total 0
lrwx------ 1 bon bon 64 Jul 24 19:10 0 -> /dev/pts/1
l-wx------ 1 bon bon 64 Jul 24 19:10 1 -> /home/bon/foo.out
lrwx------ 1 bon bon 64 Jul 24 19:10 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:10 255 -> /home/bon/lookfd.sh
$ ./lookfd.sh > foo.out 2>&1
$ cat foo.out
total 0
lrwx------ 1 bon bon 64 Jul 24 19:11 0 -> /dev/pts/1
l-wx------ 1 bon bon 64 Jul 24 19:11 1 -> /home/bon/foo.out
l-wx------ 1 bon bon 64 Jul 24 19:11 2 -> /home/bon/foo.out
lr-x------ 1 bon bon 64 Jul 24 19:11 255 -> /home/bon/lookfd.sh
您可以看到,文件描述符 1(对于STDOUT
)和 2(对于STDERR
)有所不同。为了好玩,您还可以重定向STDIN
并查看结果:
$ ./lookfd.sh < /dev/zero
total 0
lr-x------ 1 bon bon 64 Jul 24 19:18 0 -> /dev/zero
lrwx------ 1 bon bon 64 Jul 24 19:18 1 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 19:18 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:18 255 -> /home/bon/lookfd.sh
(留给读者的问题:文件描述符 255 指向哪里?;-))
答案2
不,说明书是对的。
如果一开始1指向终端,2也指向终端,那么:
command 2>&1 1>somewhere
重定向评估将从左到右进行。
因此它将首先评估2>&1
,然后首先将 fd1
用来指向的内容(即 的文件描述符the terminal
,通常是 /dev/tty)复制到 fd 中2
。
所以此时 fd2
现在指向 fd1
曾经指向 ( the terminal
)的位置
然后它评估该部分,从而将复制fd 中1>somewhere
的文件描述符(因此此时, fd现在指向,并且 fd仍然指向)somewhere
1
1
somewhere
2
the terminal
因此,它确实将 1 打印到“某处”,将 2 打印到终端,因为 2 是在 1 更改之前从 1 复制的。
另一个顺序:
command 1>somewhere 2>&1
首先将 fd 重定向1
到somewhere
,然后将相同的引用复制到 fd 2 中,因此最后 2 也指向somewhere
。但从现在起他们就不再“联系”了。每个仍然可以单独重定向。
前任:
command 1>somewhere 2>&1
exec 2>/dev/null
在那最后, fd1
指向somewhere
,并且 fd2
被定向到/dev/null
fd 的常用名称1
是 STDOUT(标准输出),fd 的常用名称2
是 STDERR(标准错误,因为它通常用于显示错误而不干扰 STDOUT)
答案3
我认为这里令人困惑的部分是误解将 stderr 重定向到 stdout 实际上连接了两个流。
这是一个完全合理的想法,但是当你写入时会发生什么,2>&1
stderr 会查看 stdout 正在写入的内容并写入到同一位置本身。因此,如果您随后告诉 stdout 去其他地方写入,它对已移动的 stderr 的目的地没有影响。
我认为这有点违反直觉,但它就是这样运作的。首先设置您要写信的位置,然后告诉每个人“复制我”。希望能澄清...
答案4
复制...
很重要,但更重要的是,它是造成许多混乱的根源。这确实很简单。这个答案只是一个“激进”的例证。
接受的答案很好,但太长并且强调“重复”。
问题明智地以以下内容结尾:
这重复一词的使用在这种情况下,我觉得有点奇怪。也许这让我很困惑。
我使用 bash 表示法并将变量“一”和“二”定义为文件句柄“1”和“2”。 (输出)重定向运算符>
是一个赋值=
。&
并$
表示“价值”。
man bash 示例(添加了默认的“1”)
ls 1>dirlist 2>&1 # both to dirlist
ls 2>&1 1>dirlist # 1 to dirlist, 2 stays on tty/screen
变得:
one=dirlist two=$one
和
two=$one one=dirlist
即使这对我和其他一些人来说也不是自动的。第一行留下$one
和$two
都包含“dirlist”。当然。
第二行以无用的赋值开始。两者的定义都以“TTY”(有点象征性)开头方向;此赋值不会更改任何值,并且对于变量(如文件句柄),没有任何内容会神奇地链接。变量two
不受以下影响one=dirlist
。当然不是。
有人(6年前)建议用“指向”代替“复制”或“重复”,然后意识到:这也会令人困惑。
甚至不需要这种重复或指针语义。也许是需要更多关注的&符号。运算符/令牌/其他内容的“值”。
当且仅当您正在寻找一种方法来获得令人惊讶的工作号码时安慰,然后是一条“完成”消息,加上一个名为“2”的文件作为奖励,然后您可以:
ls 1>2& 2>/dev/null
它自然地读作“copy"/"duplicate" 1 到 2,然后将两者一起设为 null。但这个想法是错误的,语法也是错误的。 (但没有语法错误,有效)
正确的规划方法是将两者中的任何一个重定向为 null,然后将其他重定向到同一位置:
ls 1>/dev/null 2>&1
# or
ls 2>/dev/null 1>&2
(前导“1”可以省略)
(好吧,acc.A 不太长,但列表太多 - 或者:非常好的可视化,但解释不太好)