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
的输出传递给另一个命令?
答案 0 :(得分:4)
my_command < file.txt
和cat 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
)。某些应用程序将文件映射到虚拟内存,但对stdin
或stdout
这样做是不常见的。特别是内存映射在管道上是无法实现的(无论是匿名的还是命名的),因为必须保留一系列虚拟地址,因此需要大小。
编辑:
如@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