我刚刚开始学习如何一切都是文件Linux 上的TM,这让我想知道如果我逐字读取 /dev/stdout 会发生什么:
$ cat /dev/stdout
^C
$ tail /dev/stdout
^C
(这^C
是我在程序挂起后将其杀死)。
当我尝试使用 时vim
,我收到了难以想象的消息:“/dev/stdout”不是文件。喘气!
那么,当我尝试读取这些“文件”时,为什么会出现挂断或错误消息呢?
答案1
为什么我会挂断电话
您不会从cat(1)
和中遇到“挂断” tail(1)
,它们只是阻塞读取。 cat(1)
等待输入,并在看到完整行后立即打印:
$ cat /dev/stdout
foo
foo
bar
bar
我在这里输入foo
Enterbar
EnterCTRL- D。
tail(1)
等待输入,仅在检测到时才打印EOF
:
$ tail /dev/stdout
foo
bar
foo
bar
在这里我再次输入foo
Enterbar
EnterCTRL- D。
或错误信息
Vim 是唯一一个给你错误的工具。它这样做是因为它运行 stat(2)
反对/dev/stdout
,它发现它没有S_IFREG
设置该位。
/dev/stdout
是一个文件,但不是一个常规的文件。事实上,内核中有一些舞蹈可以为其在文件系统中提供一个条目。在 Linux 上:
$ ls -l /dev/stdout
lrwxrwxrwx 1 root root 15 May 8 19:42 /dev/stdout -> /proc/self/fd/1
在 OpenBSD 上:
$ ls -l /dev/stdout
crw-rw-rw- 1 root wheel 22, 1 May 7 09:05:03 2015 /dev/stdout
在 FreeBSD 上:
$ ls -l /dev/stdout
lrwxr-xr-x 1 root wheel 4 May 8 21:35 /dev/stdout -> fd/1
$ ls -l /dev/fd/1
crw-rw-rw- 1 root wheel 0x18 May 8 21:35 /dev/fd/1
答案2
(几乎)一切都是文件,但并非一切都是文件常规的文件。在特殊文件(例如目录、网络套接字、串行端口等)上调用文本编辑器是没有意义的。
该文件/dev/stdout
可以是以下几种文件之一,具体取决于 UNIX 变体:
- “特殊”文件,通常是字符设备;
- 一个“神奇”符号链接,指向访问它的进程在此描述符上打开的文件;
- 指向上述内容之一的符号链接。
在任何情况下,打开/dev/stdout
和类似的文件都会创建一个新的文件描述符,该文件描述符与应用程序已在文件描述符 1 上打开的同一文件关联。“标准输出”表示文件描述符 1,并且这只是使用此文件描述符的约定对于输出——内核不关心。
当您在终端中运行程序时,所有三个标准描述符(0 = 标准输入、1 = 标准输出、2 = 标准错误)都会在终端设备上打开。从该设备读取会返回用户键入的字符,而写入该设备会在终端窗口中显示文本。 (对于给定的终端设备,没有标准的方法来读取它显示的输出或将输入注入其中。)
当您运行 时,它会执行与或cat /dev/stdout
完全相同的操作,因为这三个文件描述符连接到同一个文件:它告诉从终端读取。毫无争议也是如此。cat /dev/stdin
cat /dev/stderr
cat
cat
如果您运行cat /dev/stdout >foo
,/dev/stdout
则将引用该文件foo
- 该命令相当于cat foo >foo
.根据cat
实现的不同,它可能会出错(GNU 版本抱怨“输入文件是输出文件”),或者可能什么也不做,因为它从foo
空文件中读取(>foo
只是截断了它)。对于cat
未检测到这种特殊情况的版本,如果foo
不为空,则cat /dev/stdout >>foo
或等效项cat foo >>foo
会将文件的内容无限期地附加到自身。
当您运行时vim /dev/stdout
,它会抱怨,因为它不知道如何编辑终端(这没有意义)。
答案3
cat
并tail
正在寻找可选内容,后跟文件结尾。/dev/stdout
仍然开放,所以cat
继续tail
寻找。