1. 考虑片段#1:
$ cat test.txt > test.txt
cat: test.txt: input file is output file
似乎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
$
在这里我们看到以sed
读取模式打开 test.txt (最后一个参数),同时设置test.txt
为其文件输出描述符。此外,操作员会在开始读取'>'
文件之前覆盖文件的内容。sed
我知道管道中的命令是并行执行的,但没有遇到任何有关重定向运算符如何行为的信息。任何支持链接都会有所帮助。
答案1
除了 jordanm 指出的文档之外,我想确保纠正您的问题中说明的误解 - 执行的程序确实不是处理重定向。它甚至几乎不知道它们。shell 处理重定向。
程序启动时打开三个文件:stdin (#0)、stdout (#1) 和 stderr (#2)。如果您只是从 shell 提示符运行一个程序,这些程序将连接到您的终端设备,因此该程序会读取您键入的内容 (stdin),并将输出 (stdout) 和错误 (stderr) 打印到您的终端。
举个例子,我只是cat
在终端中运行(上面tty
写着 is /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
事实上,我们可以看到它的终端对所有三个都开放。现在,让我们尝试一个相当愚蠢的 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
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
请注意 shell 如何打开一个管道,并使用它来将 的 stdout 连接cat
到 的 stdin dd
。以及它如何将dd
stdout 连接到/dev/null
.
正在运行的命令并不真正了解重定向。他们只是像平常一样使用 stdin、stdout、stderr。这些都可以是终端,或者它们可以被重定向到/来自文件、设备,或者可能是到另一个程序的管道。或者甚至是网络套接字(如果您的 shell 支持的话)。
即使是最复杂的管道实际上也只是向 shell 发出的指令,说明如何在执行程序之前连接这三个文件句柄。
(注意:某些程序在其中一个连接到终端的情况下表现不同,但这通常在交互式使用中更加用户友好。例如,ls
当标准输出不是一个时,切换到单列输出并且没有颜色终端——如果您要将其传递给另一个程序,这通常是您想要的。如果 stdin 不是终端,某些程序会以不同的方式处理提示,等等。)
答案2
发生重定向第一的在壳里。在你的例子中:
cat test.txt > test.txt
首先发生的是 bash opens test.txt
,它会截断文件。现在它是空的,之前作为参数cat
执行。test.txt
从 bash 联机帮助页的重定向部分:
在执行命令之前,可以使用 shell 解释的特殊符号来重定向其输入和输出。重定向还可用于打开和关闭当前 shell 执行环境的文件。以下重定向运算符可以出现在简单命令之前或出现在命令内的任何位置,也可以跟随在命令之后。重定向按照它们出现的顺序从左到右进行处理。
我在 POSIX 规范中没有看到任何内容表明它应该首先发生,但我不知道有哪个 shell 不这样做。