Linux Do';cat foo.txt | myu cmd';和';我的命令<;foo.txt';完成同样的事情?

Linux Do';cat foo.txt | myu cmd';和';我的命令<;foo.txt';完成同样的事情?,linux,bash,redirect,pipe,stdin,Linux,Bash,Redirect,Pipe,Stdin,帮助我理解了重定向和管道之间的区别,但示例重点介绍了重定向STDOUT(echo foo>bar.txt)和管道STDIN(ls | grep foo) 在我看来,任何可以写入my_command

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

在我看来,任何可以写入
my_command
的命令也可以写入
cat file.txt | my_command
。在什么情况下需要标准数据重定向


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

当然,您可以使用从
cat
读取的管道来替换输入重定向的任何使用,但这样做效率很低,因为您正在生成一个新进程来完成shell本身已经可以完成的事情。然而,并不是所有的
cat…|my_命令
可以替换为
my_命令<…
,也就是说,当
cat
执行其连接两个(或更多)文件的预期工作时,将其输出通过管道传输到另一个命令是完全合理的

cat file1.txt file2.txt | my_command

my_command
cat file.txt | my_command
之间有什么区别

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

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

因此,理论上,管道效率较低。这种差异是否显著取决于许多因素,包括“显著”的定义。管道的最佳使用时间为:

command1 > file.txt
command2 < file.txt
更有效,请记住,在实践中,我们可能需要在
rm file.txt
中使用第三个子进程

但是,管道也有一些限制。它们不可查找(随机访问,请参阅
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
中,子shell是一个子进程,在父进程中不会更改
i
(注意:这不适用于korn shell,
ksh

读取时
做
((i=i+1))
完成

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

重定向标准输入肯定比管道版本更可取,因为它不会产生不必要的进程。更好的问题是何时需要管道。@chepner-当命令生成STDIN上由
my_命令读取的数据时,管道当然是必要的。我可以看到将文件重定向到STDIN(或者是“将STDIN重定向到文件”)是多么的高效。我想知道的是,在某些情况下,您是否不能简单地通过管道将文件传输到STDIN,而必须使用STDIN重定向方法。在我的问题中,我应该如何更好地表达这一点?重定向从来都不是必需的,但您的问题意味着您认为管道更好,应该尽可能避免重定向。恰恰相反:您应该尽可能使用重定向,并且仅在必要时使用管道。当shell完全能够自己打开文件时,管道使用
cat
打开文件进行读取。如果您的程序多次尝试
seek()
或读取其输入文件的一部分,则无法使用管道。这意味着,例如,可以通过启动多个处理不同输入文件块的进程来并行化的
sort
版本在读取
cat
(至少没有从FIFO读取所有文件到一个临时文件,而不是仅仅能够将每一个线程/子进程SeCK)直接输入到一个不同的输入块中。另一个例子考虑<代码> WC--C<代码>——给定一个实际文件的句柄,它可以只使用<代码>状态()
-家族调用以恒定时间获取文件长度,无论需要多长时间。给定管道,它必须将整个内容读取到底并计算字节数,因此我们不仅讨论FIFO开销,还必须使用完全不同的算法开销。我不同意这一点适用于任何无条件重定向。对于progr来说,这是罕见的ams在其stdin中的
fstat
seek
(性能优化除外),但并非不可能。试着记住一个标准GNU工具的名称,如果给它一个管道,它会将管道的全部内容复制到一个临时文件中,以便在它能够开始工作之前有一个可查找的源…
shuf
,也许吧?@CharlesDuffy:这些正是我感兴趣听到的情况。这有助于提醒我有时可以将标准数据附加到文件上,并将其视为文件。我习惯于将其视为终端或管道的接收端,以至于我忘记了
command1 | command2
i=0
cat file.txt|while read
do
   (( i = i+1 ))
done

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

echo "$i"