2.19 重定向看起来不起作用时保存输出

2.19 Saving Output When Redirect Doesn’t Seem to Work

问题

你尝试使用 “>”,但某些(或全部)输出依然出现在屏幕上。

例如,编译器依然会产生错误信息:

你想捕获这些信息,所以你尝试重定向输出:

然而,重定向输出似乎不起作用。实际上,当你检查重定向目标文件,你会发现文件是空的(零字节长度):

解决方案

重定向错误输出,如下所示:

现在,save.it 的内容就是此前看到的错误信息。

讨论

所以,这是怎么回事呢?Unix 和 Linux 的每个进程启动时通常伴随着三个打开的文件描述符:第一个用于输入,称为标准输入(STDIN);第二个用于输出,称为标准输出(STDOUT);第三个用于错速信息,称为标准错误(STDERR)。编写任何程序都要将错误消息写入标准错误,并将正常的预期输出写入标准输出,所有程序员都应遵守这些规定。但因并非强制,所以无法保证您获得的每条错误消息都将转到标准错误。但是大多数历史悠久的软件都表现得很好。这也就是为什么这些编译器的信息并未简单地使用 “>” 重定向;而是只重定向标准输出,没有包含标准错误。

正如前述文章提到的,每个文件描述符都用数字表示,数字从 0 开始。标准输入是 0,输出是 1,错误是 2。这意味着你重定向到标准输出时可以稍微更详细一些:1> (而不是简单地用 >)后跟一个文件名,但并没有这样的必要。简写为 > 就行。要重定向到标准输出,则使用 2>。

标准输出和标准错误之间的一个重要区别是,标准输出被缓冲,但标准错误是无缓冲的;也就是说,每个字符彼此独立输出,所有字符并非收集在一起然后一次性输出。这意味着,当错误发生时你会立刻看到错误消息,这样错过错误消息的机会就会变少,但代价之一则是效率降低。这并非在于标准输出不可靠,问题在于出错环境(例如,当程序意外终止时)。缓存后的输出可能不会在程序终止时输出到屏幕。这也就是为什么不缓冲标准输出:确保消息能够输出。相反,在标准输出中,只有当缓冲区满了(或文件关闭)之后才会实际输出。如果频繁使用输出时,缓存输出效率会更高,但是要报告错误时,效率就不那么重要了。

如果在保存输出的同时也想查看输出呢?在《2.16 即使在将输出用作输入时也要保存副本》中提到过的 tee 命令似乎能解决这个问题:

此命令会将标准错误重定向到标准输出,然后将二者一同输入 tee。tee 命令在把其输入写入文件(save.it)的同时,还会写入到标准输出,也就是说,在没有被再次重定向时会输出到屏幕。

有这么一个重定向的特例,体现出重定向的顺序非常重要。比较以下两条命令:

第一条命令,标准输入重定向到文件 my.file 中,然后标准错误重定向到与标准输出相同的位置。所有的输出都会出现在 my.file 中。

但是第二条命令不是这种情况。在第二条命令中,标准错误被重定向到了标准输出(此时标准输出连接到屏幕),之后标准输出被重定向到 my.file。因此,只有标准输出消息会被放入文件中,而此时出现的错误仍将显示在屏幕上。

然而,这个顺序在进入管道后会被打破——你不能把第二个重定向放到管道符号之后,因为管道后是第二条命令了。所以,当你写出以下命令时,bash 会出现异常:

bash 会认为标准输出应当被推入管道。因此,bash 假设您在写下 2>&1 时,是想要把标准错误包含到管道中,即使它的正常排序并非如此。

这个和管道语法的另一个结果是,它使我们只能把标准输出传递到管道,而无法囊括标准错误——除非我们首先交换文件描述符(见下一节)。

从 4.x 版本的 bash 开始,有一种快捷语法,可以将标准输出和标准错误重定向到管道中。要将两个输出流从 somecmd 重定向到某个 othercmd,如前所示,我们现在可以使用 |& 来编写

参见

  • 2.17 使用输出作为参数连接两个程序
  • 2.20 互换 STDERR 和 STDOUT

关于 “2.19 重定向看起来不起作用时保存输出” 的 1 个意见

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据