摘自回答:
1号航站楼:
[ciupicri@hermes ~]$ cat
shows on the tty but bypasses cat
2 号航站楼:
[ciupicri@hermes ~]$ pidof cat
7417
[ciupicri@hermes ~]$ echo "shows on the tty but bypasses cat" > /proc/7417/fd/0
我不太明白为什么写入对应于stdin
进程的文件描述符cat
会绕过进程本身,但会出现在终端上。terminal
、file descriptor
、之间的关系让我感到困惑。此外,我觉得device file
这些console
在技术写作中有时会被滥用。有人能告诉我吗?
答案1
您对标准文件描述符的想法是错误的。
STDIN 指向一个输入设备。如果您获取了属于另一个进程的 STDIN,则可以使用它来窃取该进程的输入。您不应该写入它,如果您这样做,也没有特别的理由期望该输出最终会被目标进程读取;这只有在设备恰好将其输出循环回其输入时才会发生。
类似地,STDOUT 指向输出设备。如果您获取了属于另一个进程的 STDOUT,则可以使用它来生成输出,该输出将发送到另一个进程正在使用的同一文件或终端。您不应该从中读取,如果您这样做,您不太可能看到该进程的输出。
在这个特定场景中,正如 Håkan 指出的那样,STDIN 和 STDOUT 都指向同一个设备,因此不应该写入 STDIN,如果这样做,其效果与写入 STDOUT 相同,即,它将输出直接发送到终端。如果标准文件描述符是其他内容,例如,如果您使用文件重定向或管道,则写入 STDIN 与写入 STDOUT 不同。
可以这样想:如果您的进程写入自己的标准输入,您通常不会期望将该输出视为新输入。另一个进程执行写入操作这一事实不会改变这一点。
尤其,如果您的进程在虚拟终端上运行(没有任何重定向),那么当您写入标准输出时,您希望该输出显示在终端上,而不是反馈到您的输入中。您错误地使用终端的标准输入文件描述符而不是标准输出文件描述符这一事实不会改变这一点,并且哪个进程正在执行写入也无关紧要。(*)
考虑一些进程写入其自己的标准输入的具体示例可能会有所启发。
如果标准输入已从文件重定向会怎样?
foo@bar:~/test$ cat hello
hi
foo@bar:~/test$ (cat; echo hello there > /dev/stdin; cat) < hello
hi
lo there
文件的内容将被覆盖;如果进程没有先完成文件读取,或者文件现在比以前长,它将读取刚刚写入的部分或全部内容。请注意,因为我们已经读取了“hi”和换行符,所以当我们继续读取时,我们会跳过“hello there”的前三个字符。
如果标准输入是管道怎么办?
foo@bar:~/test$ echo weird | (echo hello > /dev/stdin; cat)
weird
hello
显然,Linux 管道设备确实会将其输出循环回其输入,但我不确定此行为是否由 POSIX 保证或取决于特定实现。如果我是你,我会避免使用这个技巧!
那么向进程发送一些输入的正确方法是什么?
嗯,其中一个选项是这个答案。
另一个是正确使用管道,例如,
echo I'm sending some input | cat > myinput
这里echo
进程正在向进程发送输入cat
。这可以保证正常工作,因为echo
正在将数据发送到指向管道一端的文件描述符,并cat
从指向管道另一端的文件描述符接收数据。
两种情况下的原理都是一样的。目标进程正在从特定设备读取数据,我们需要让该设备生成我们想要的输出。具体怎么做取决于设备是什么。
(*)虚拟终端可以被设计为为 shell 的标准输入和标准输出提供不同的设备,并且连接到标准输入的设备可以被编程为将任何写入它的字符循环回输入流。但它没有有以这种方式工作,但实际上并没有,也许是因为第一个虚拟终端的主要设计目标是以与老式的物理终端他们正在替换它。
答案2
首先,根据观察到的行为,我认为这与 Linux 有关。在不同的操作系统下,我预计事情可能会有所不同(无论是好是坏)。
我相信从中得出的最实用的结论可能是,这不是与流程交互的正确方式。
这里造成混淆的主要原因似乎是您对 Linux /proc 文件系统的期望高于它实际提供的功能。
如果你仔细观察 /proc 文件系统中实际有什么,你会看到类似这样的内容:
$ ls -l /proc/29058/fd/
total 0
lrwx------ 1 user1 user1 64 Jan 1 20:39 0 -> /dev/pts/2
lrwx------ 1 user1 user1 64 Jan 1 20:39 1 -> /dev/pts/2
lrwx------ 1 user1 user1 64 Jan 1 20:39 2 -> /dev/pts/2
$
正如您所见,/proc 中此进程的所有“标准”fds(0 / stdin、1 / stdout、2 / stderr)的表示都是符号链接,它们都链接到完全相同的东西,即终端(伪终端从属)。
cat
也就是说,您从来就没有向进程的标准输入写入数据,而是向进程所在的终端写入数据。
这并不是因为标准输入的概念以某种奇怪的方式工作,而是因为 Linux /proc 文件系统只是链接到进程正在读取的任何内容作为其标准输入。
在这种情况下,当您的进程从终端读取数据作为其标准输入时,链接指向一个设备节点,该节点代表该终端的输入(从设备读取时)和输出(写入设备时)方面,因此,当您从进程的标准输入读取数据时,此节点仅与进程的标准输入相关,向其写入数据与进程的标准输入无关。