'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.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 内置函数,它仍然是 运行 在子进程中。因此我们有两个子进程。
所以从理论上讲,管道效率较低。这种差异是否显着取决于许多因素,包括 "significant" 的定义。管道 是 更可取的时间是这个替代方案:
command1 > file.txt
command2 < file.txt
这里很可能
command1 | command2
效率更高,请记住,在实践中,我们可能需要 rm file.txt
中的第三个子进程。
但是,管道也有限制。它们不是 seekable(随机访问,参见 man 2 lseek
)并且它们不能 memory mapped(参见 man 2 mmap
)。一些应用程序将文件映射到虚拟内存,但将文件映射到 stdin
或 stdout
是不常见的。内存映射在管道(无论是匿名的还是命名的)上是不可能的,因为必须保留一系列虚拟地址并且需要一个大小。
编辑:
正如@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"
这显示了正确的计数,因为没有涉及子进程。
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.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 内置函数,它仍然是 运行 在子进程中。因此我们有两个子进程。
所以从理论上讲,管道效率较低。这种差异是否显着取决于许多因素,包括 "significant" 的定义。管道 是 更可取的时间是这个替代方案:
command1 > file.txt
command2 < file.txt
这里很可能
command1 | command2
效率更高,请记住,在实践中,我们可能需要 rm file.txt
中的第三个子进程。
但是,管道也有限制。它们不是 seekable(随机访问,参见 man 2 lseek
)并且它们不能 memory mapped(参见 man 2 mmap
)。一些应用程序将文件映射到虚拟内存,但将文件映射到 stdin
或 stdout
是不常见的。内存映射在管道(无论是匿名的还是命名的)上是不可能的,因为必须保留一系列虚拟地址并且需要一个大小。
编辑:
正如@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"
这显示了正确的计数,因为没有涉及子进程。