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

时间:2018-01-25 15:49:55

标签: linux bash redirect pipe stdin

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的输出传递给另一个命令?

2 个答案:

答案 0 :(得分:4)

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,它仍然在子进程中运行。因此,我们有两个子流程。

因此,管道在理论上效率较低。这种差异是否显着取决于许多因素,包括&#34;重要&#34;的定义。管道 的时间是另一种选择:

command1 > file.txt
command2 < file.txt

这可能是

command1 | command2

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

但是,管道存在限制。它们不是 seekable (随机访问,请参阅man 2 lseek),它们不能内存映射(请参阅man 2 mmap)。某些应用程序将文件映射到虚拟内存,但对stdinstdout这样做是不常见的。特别是内存映射在管道上是无法实现的(无论是匿名的还是命名的),因为必须保留一系列虚拟地址,因此需要大小。

编辑:

如@JohnKugelman所述,许多SO问题的常见错误和来源是子进程和重定向的相关问题:

使用99行文件file.txt

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

echo "$i"

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

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

echo "$i"

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

答案 1 :(得分:1)

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

cat file1.txt file2.txt | my_command