昨天我读到这个评论这表示在 shell 中(至少bash
)>&-
“具有与”相同的结果>/dev/null
。
该评论实际上指的是ABS 指南作为其信息来源。但该消息来源称该>&-
语法“关闭文件描述符”。
我不清楚关闭文件描述符和将其重定向到空设备这两个操作是否完全等效。所以我的问题是:是吗?
从表面上看,关闭描述符就像关上一扇门,但将其重定向到空设备就像打开一扇通往地狱的门!对我来说,两者似乎并不完全相同,因为如果我看到一扇关着的门,我不会尝试扔出任何东西,但如果我看到一扇开着的门,我会认为我可以。
换句话说,我一直想知道>/dev/null
这是否意味着cat mybigfile >/dev/null
实际上会处理文件的每个字节并将其写入到/dev/null
忘记它的地方。另一方面,如果 shell 遇到一个关闭的文件描述符,我倾向于认为(但不确定)它根本不会写任何东西,尽管问题仍然是是否cat
仍然会读每个字节。
这条评论说>&-
和>/dev/null
“应该“是一样的,但这对我来说不是那么响亮的答案。我希望有一个更权威的答案,并参考标准或源核心或不......
答案1
不,你当然不想要关闭文件描述符 0、1 和 2。
如果这样做,应用程序第一次打开文件时,它将变成 stdin/stdout/stderr...
例如,如果您这样做:
echo text | tee file >&-
当tee
(至少某些实现,如 busybox')打开文件进行写入时,它将在文件描述符 1(stdout)上打开。因此tee
将写入text
两次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
.
一些工具甚至一些库试图防止这种情况发生。例如,tee
如果 GNU 打开用于写入的文件被分配为 0、1、2,则 GNU 会将文件描述符移动到 2 以上,而 busybox 则tee
不会。
大多数工具,如果它们无法写入标准输出(因为例如它没有打开),将在标准错误上报告错误消息(以用户的语言,这意味着打开和解析本地化文件的额外处理......),所以它将显着降低效率,并可能导致程序失败。
无论如何,效率都不会更高。该程序仍然会进行write()
系统调用。只有当程序在第一次失败的系统调用后放弃写入 stdout/stderr 时,效率才会更高write()
,但程序通常不会这样做。他们通常要么因错误退出,要么继续尝试。
答案2
IOW我一直想知道
>/dev/null
这是否意味着cat mybigfile >/dev/null
实际上会处理文件的每个字节并将其写入到/dev/null
忘记它的地方。
这不是您问题的完整答案,但是是的,以上就是它的工作原理。
cat
读取指定的文件,如果没有指定文件则读取标准输入,并输出到其标准输出这些内容,直到在最后一个命名的文件(包括标准输入)上遇到 EOF。那是它是工作。
通过添加,>/dev/null
您可以将标准输出重定向到 /dev/null。这是一个特殊的文件(设备节点),它会丢弃写入其中的任何内容(并在读取时立即返回 EOF)。请注意,I/O 重定向是 shell 提供的功能,而不是每个单独的应用程序提供的功能,并且 I/O 重定向没有什么神奇之处。姓名/dev/null,仅发生在大多数情况下的情况类Unix系统。
还需要注意的是,设备节点的具体机制因操作系统而异,但 cat(在 GNU 系统中表示 coreutils)是跨平台的(相同的源代码需要运行至少在 Linux 和 Hurd 上),因此不能依赖于特定的操作系统内核。此外,如果您使用另一个名称创建 /dev/null 别名(在 Linux 上,这意味着具有相同主/次设备号的设备节点),它仍然有效。而且总是有这样的情况:在其他地方编写的行为实际上是相同的(例如,/dev/zero)。
由此可见,它cat
不知道 /dev/null 的特殊属性,而且实际上很可能一开始就不知道重定向,但是它仍然需要执行完全相同的工作:它读取指定的文件,并将这些文件的内容输出到其标准输出。标准输出cat
碰巧进入空洞并不是它cat
本身所关心的。