'cat foo.txt | my_cmd' 和 'my_cmd < foo.txt' 完成同样的事情吗?

Do 'cat foo.txt | my_cmd' and 'my_cmd < foo.txt' accomplish the same thing?

This question 帮助我理解了重定向和管道之间的区别,但示例侧重于重定向 STDOUT (echo foo > bar.txt) 和管道 STDIN (ls | grep foo)。

在我看来,任何可以写成 my_command < file.txt 的命令也可以写成 cat file.txt | my_command。在什么情况下需要 STDIN 重定向?

除了使用 cat 产生一个额外的进程并且比重定向 STDIN 效率低之外,在某些情况下您 必须 使用 STDIN 重定向?换句话说,是否有理由将 cat 的输出通过管道传递给另一个命令?

您当然可以用从 cat 读取的管道替换任何输入重定向的使用,但这样做效率低下,因为您正在生成一个新进程来执行 shell ] 已经可以自己做。但是,并非 cat ... | my_command 每个 实例都可以替换为 my_command < ...,即当 cat 正在执行其连接两个(或更多)的预期工作时文件,将其输出通过管道传输到另一个命令是完全合理的。

cat file1.txt file2.txt | my_command

my_command < file.txtcat file.txt | my_command有什么区别?

my_command < file.txt 

重定向符号也可以写成 0<,因为这会将文件描述符 0 (stdin) 重定向到 file.txt 而不是当前设置,这可能是终端.如果 my_command 是 shell 内置的,则没有创建子进程,否则有一个。

cat file.txt | my_command

这会将左侧命令的文件描述符 1 (stdout) 重定向到匿名管道的输入流,并将右侧命令的文件描述符 0 (stdin) 重定向到匿名管道的输出流。

我们立即看到有一个子进程,因为 cat 不是 shell 内置的。但是在 bash 中,即使 my_command 是一个 shell 内置函数,它仍然是 运行 在子进程中。因此我们有两个子进程。

所以从理论上讲,管道效率较低。这种差异是否显着取决于许多因素,包括 "significant" 的定义。管道 更可取的时间是这个替代方案:

command1 > file.txt
command2 < file.txt

这里很可能

command1 | command2

效率更高,请记住,在实践中,我们可能需要 rm file.txt 中的第三个子进程。

但是,管道也有限制。它们不是 seekable(随机访问,参见 man 2 lseek)并且它们不能 memory mapped(参见 man 2 mmap)。一些应用程序将文件映射到虚拟内存,但将文件映射到 stdinstdout 是不常见的。内存映射在管道(无论是匿名的还是命名的)上是不可能的,因为必须保留一系列虚拟地址并且需要一个大小。

编辑:

正如@JohnKugelman 所提到的,许多 SO 问题的一个常见错误和根源是与子进程和重定向相关的问题:

取一个文件 file.txt 有 99 行:

i=0
cat file.txt|while read
do
   (( i = i+1 ))
done

echo "$i"

显示什么?答案是0。为什么?因为计数 i = i + 1 是在 subshell 中完成的,而在 bash 中它是一个子进程并且不会更改 i在父项中(注意:这不适用于 korn shell、ksh)。

while read
do
   (( i = i+1 ))
done < file.txt

echo "$i"

这显示了正确的计数,因为没有涉及子进程。