重定向操作符总是并行打开文件描述符吗?

Ken*_*war 8 shell io-redirection

1. 考虑片段#1:

$ cat test.txt > test.txt
cat: test.txt: input file is output file
Run Code Online (Sandbox Code Playgroud)

似乎cat使它的输入文件描述符指向 test.txt,然后当它尝试将其输出文件描述符设置为 test.txt 时,它会引发上述错误。这里似乎cat知道重定向运算符,因此继续尝试将输出文件描述符设置为 test.txt

2. 考虑片段#2:

$ cat 1.txt
1:CAT
2:dog
$ sed 's/cat/CAT/g' test.txt
1:CAT
2:dog
$ sed 's/cat/CAT/g' test.txt > test.txt
$ cat test.txt # Note that test.txt is now empty
$
Run Code Online (Sandbox Code Playgroud)

在这里我们看到sed以读取模式打开 test.txt(最后一个参数)并同时设置test.txt为其文件输出描述符。此外,'>'操作员会在sed开始读取文件之前覆盖文件的内容。

我知道管道中的命令并行执行,但没有遇到有关重定向运算符行为方式的任何信息。任何支持链接都会有所帮助。

der*_*ert 14

除了文档jordanm点,我想一定要纠正你的问题,执行的程序也说明一种误解,处理重定向。它甚至几乎不知道它们。shell 处理重定向。

程序启动时打开三个文件:stdin (#0)、stdout (#1) 和 stderr (#2)。如果您只是从 shell 提示符运行程序,它们将连接到您的终端设备,因此程序会读取您键入的内容 (stdin),并将输出 (stdout) 和错误 (stderr) 打印到您的终端。

例如,我只是cat在终端中运行(上面tty写着是/dev/pts/31)。我可以检查它打开了哪些文件lsof

$ lsof -a -p `pidof cat` -d0,1,2
COMMAND   PID    USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
cat     21257 anthony    0u   CHR 136,31      0t0   34 /dev/pts/31
cat     21257 anthony    1u   CHR 136,31      0t0   34 /dev/pts/31
cat     21257 anthony    2u   CHR 136,31      0t0   34 /dev/pts/31
Run Code Online (Sandbox Code Playgroud)

事实上,我们可以看到它为这三个终端都打开了终端。现在,让我们尝试一个相当愚蠢的 cat 调用:cat < /dev/zero > /dev/null 2>/dev/full,它重定向所有三个:

COMMAND   PID    USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
cat     21838 anthony    0r   CHR    1,5      0t0 1030 /dev/zero
cat     21838 anthony    1w   CHR    1,3      0t0 1028 /dev/null
cat     21838 anthony    2w   CHR    1,7      0t0 1031 /dev/full
Run Code Online (Sandbox Code Playgroud)

shell 通过将三个设备作为 stdin、stdout 和 stderr(而不是终端)传递来实现这些重定向。shell 类似地实现了管道。让我们尝试一下cat | dd > /dev/null(确实是一个相当愚蠢的管道):

COMMAND   PID    USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
cat     22507 anthony    0u   CHR 136,31      0t0       34 /dev/pts/31
cat     22507 anthony    1w  FIFO    0,8      0t0 56081395 pipe
cat     22507 anthony    2u   CHR 136,31      0t0       34 /dev/pts/31

COMMAND   PID    USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
dd      22508 anthony    0r  FIFO    0,8      0t0 56081395 pipe
dd      22508 anthony    1u   CHR 136,31      0t0       34 /dev/null
dd      22508 anthony    2u   CHR 136,31      0t0       34 /dev/pts/31
Run Code Online (Sandbox Code Playgroud)

请注意 shell 如何打开管道,并使用它来将 的标准输出连接cat到 的标准输入dd。以及它是如何将dd标准输出连接到/dev/null.

正在运行的命令并不真正了解重定向。他们只是像往常一样使用标准输入、标准输出、标准错误。这些可能都是终端,或者它们可以重定向到/从文件、设备或管道重定向到另一个程序。甚至是网络套接字,如果您的 shell 支持的话。

即使是最可笑的复杂管道实际上也只是在执行程序之前向 shell 发送有关如何连接这三个文件句柄的指令。

(注意:有些程序在其中一个连接到终端的情况下表现不同,但这通常在交互使用中对用户更友好。例如,ls当 stdout 不是终端 - 如果您要将其传递给另一个程序,这通常是您想要的。如果 stdin 不是终端,某些程序会以不同的方式处理提示。等等。)


jor*_*anm 12

重定向首先发生在 shell 中。在你的例子中:

cat test.txt > test.txt
Run Code Online (Sandbox Code Playgroud)

发生的第一件事是 bash opens test.txt,它会截断文件。它现在是空的,在作为参数cat执行之前test.txt

从 bash 联机帮助页的重定向部分:

在执行命令之前,可以使用由 shell 解释的特殊符号重定向其输入和输出。重定向还可用于打开和关闭当前 shell 执行环境的文件。以下重定向操作符可能出现在简单命令的任何位置之前或出现,也可能出现在命令之后。重定向按照它们出现的顺序进行处理,从左到右。

我在 POSIX 规范中没有看到任何内容表明它应该首先发生,但我不知道它没有的外壳。