>&-是否比> / dev / null更有效?


58

昨天我读了这样的SO评论,它说在shell中(至少bash>&-“具有与...相同的结果” >/dev/null

该评论实际上是将ABS指南作为其信息来源。但是那个消息来源说>&-语法“关闭文件描述符”。

我不清楚关闭文件描述符并将其重定向到空设备这两个动作是否完全等效。所以我的问题是:是吗?

从表面上看,关闭描述符就像关闭一扇门,但是将其重定向到一个空设备则意味着陷入困境。这两个对我来说似乎并不完全相同,因为如果我看到一扇关闭的门,我不会尝试扔掉任何东西,但是如果我看到一扇敞开的门,我会假设我可以。

换句话说,我一直想知道是否>/dev/null意味着cat mybigfile >/dev/null将实际上处理文件的每个字节并将其写入/dev/null而忘记了它。另一方面,如果外壳遇到一个封闭的文件描述符,我倾向于认为(但不确定)它将根本不写任何内容,尽管问题仍然在于是否cat仍将读取每个字节。

这个评论>&->/dev/null应该 ”相同,但是对我来说却不是那么响亮的答案。我想有一个更权威的答案,参考或不参考标准或源核心。


如果我想慈善,我想说的是,这并不意味着它们的实现方式相同,只是它们的最终结果相同,从而使您无法看到程序的输出。
Barmar 2014年

Answers:


71

不,你当然希望关闭文件描述符0,1和2。

如果这样做,应用程序第一次打开文件时,它将变为stdin / stdout / stderr ...

例如,如果您这样做:

echo text | tee file >&-

tee(至少某些实现,例如busybox')打开文件进行写入时,它将在文件描述符1(stdout)上打开。因此teetext两次写入file

$ echo text | strace tee file >&-
[...]
open("file", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 1
read(0, "text\n", 8193)                 = 5
write(1, "text\n", 5)                   = 5
write(1, "text\n", 5)                   = 5
read(0, "", 8193)                       = 0
exit_group(0)                           = ?

众所周知,这会导致安全漏洞。例如:

chsh 2>&-

并且chsh(setuid应用程序)最终可能会在中写入错误消息/etc/passwd

一些工具,甚至某些库都试图防止这种情况。例如,如果GNU tee将打开的用于写入的文件分配为0、1、2,而忙碌的框tee未分配,则GNU 会将文件描述符移动到2以上。

大多数工具,如果它们不能写到stdout(例如因为它没有打开),将在stderr上报告错误消息(使用用户的语言,这意味着要进行更多处理以打开和解析本地化文件...),因此这将大大降低效率,并可能导致程序失败。

无论如何,它都不会更有效率。该程序仍将进行write()系统调用。仅当程序在第一次失败的write()系统调用后放弃对stdout / stderr的写入时,效率才会更高,但是程序通常不会这样做。他们通常要么退出并出现错误,要么继续尝试。


4
我认为,如果最后一段位于顶部,则该答案会更好(因为这是最直接回答OP的问题),然后继续讨论为什么即使是大多数情况下它也是一个坏主意。但我接受,投票赞成。;)
CVn 2014年

@StéphaneChazelas:正如Michael所说,我期望最后一个段落在最上面,但是感谢您澄清它实际上只会造成问题。因此,我想一个辅助问题是,何时关闭FD真正有用?还是我应该单独提出一个问题?
jamadagni 2014年


1
@jamadagni如果Stéphane提供的链接没有回答问题,我想这听起来像是一个单独问题的开头,因为它与这两种方法的相对效率没有直接关系。
CVn 2014年

1
我感谢Stephane从此版本的重要安全警告开始,因为如果最后一段放在最前面,它将不太明显。向我+1。
Olivier Dulac 2014年

14

IOW我一直想知道是否>/dev/null意味着它cat mybigfile >/dev/null会真正处理文件的每个字节并将其写入/dev/null而忘记了。

这不是您问题的完整答案,但是可以,以上是它的工作原理。

cat读取已命名的文件,如果没有命名文件,则读取标准输入,并将内容输出到其标准输出,直到遇到最后一个文件的EOF(包括标准输入)。那是它的工作。

通过添加,>/dev/null您会将标准输出重定向到/ dev / null。那是一个特殊的文件(设备节点),它将丢弃其中写入的所有内容(并在读取后立即返回EOF)。请注意,I / O重定向是外壳程序提供的功能,而不是每个单独的应用程序提供的功能,名称 / dev / null 并不是什么神奇的东西,只有在大多数类似Unix的系统上才存在。

还需要注意的是,设备节点的具体机制因操作系统而异,但是cat(在GNU系统中,意味着coreutils)是跨平台的(相同的源代码至少需要在Linux和Linux上运行) Hurd),因此不能依赖于特定的操作系统内核。此外,如果您使用其他名称创建/ dev / null别名(在Linux上,这意味着具有相同的主要/次要设备编号的设备节点),它仍然可以使用。而且总是有在其他地方编写实际上表现相同(例如,/ dev / zero)的情况。

随之而来的是,cat它没有意识到/ dev / null的特殊属性,并且实际上一开始就没有意识到重定向,但是它仍然需要执行完全相同的工作:它读取命名的文件,并输出文件的内容。该文件/这些文件到其标准输出。标准输出的结果cat变成空虚cat本身并不是问题。


2
扩展您的答案:是的,cat mybigfile > /dev/null将导致cat将每个字节读bigfile入内存。并且,对于每个n字节,它将调用write(1, buffer, n)。该cat程序不为人所知,write它将完全不起作用(除了一些琐碎的簿记之外)。写入/dev/null不需要处理每个字节。
G-Man

2
我记得当我读到/ dev / null设备的Linux内核源代码时感到非常震惊。我原以为会有一些精心设计的free()缓冲区等系统,但是,不,它基本上只是一个return()。
Brian Minton 2014年

4
@ G-Man:我不知道您可以保证在所有情况下都是如此。现在我无法找到证据,但我记得的任何部分实施catcp将由工作mmap荷兰国际集团的源文件的大块到内存中,然后调用write()上的映射区域。如果您正在写入/dev/null,则write()调用将立即返回,而不会在源文件的页面中产生错误,因此实际上不会从磁盘读取该调用。
Nate Eldredge 2014年

2
同样,类似GNU的东西cat确实可以在许多平台上运行,但是对源代码的随意浏览会显示很多#ifdefs:从字面上看,它并不是在所有平台上运行的相同代码,并且有很多与系统有关的部分。
Nate Eldredge 2014年

@NateEldredge:有趣的一点是,但是我只是基于Michael的答案,因此,您与Michael的矛盾并不那么严重。
G-Man
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.