问题
为什么 shell 会实现诸如<<<
,< <(command)
和之类的替代方法< /dev/fd/*
,以将某些内容重定向到stdin
管道确实存在的情况?
例子
方式|
(经典管道)
echo 'text' | sed 's/x/y/'
# or
cat - | sed 's/x/y/' # type text afterwards
道路<<<
sed 's/x/y/' <<< 'text'
道路< <(command)
sed 's/x/y/' < <(echo 'text')
# while <(command) becomes a file descriptor like
sed 's/x/y/' < /dev/fd/42
全部归来teyt
。
答案1
索托主要考虑的是美学原因,但也有技术原因。例如,管道通常会在右侧产生一个子流程,这意味着您无法设置您希望稍后可用的状态或变量。比较:
$ echo foo > file
$ line=bar; cat file | read -r line; echo "$line"
bar
$ line=bar; read -r line < file; echo "$line"
foo
答案2
因为管道必须由进程供给。重定向<
不需要进程,它可以由文件提供。重定向<
也是在<<<
和<( ...)
操作发明之前很久就创建的。
它也是>
输出重定向的补充(对应)。拥有相互补充的基本功能是一件好事。
答案3
当从文件重定向输入时,使用program <file
比cat file | program
.除了效率之外(正如 Sotto Voce 所指出的),它还允许程序直接访问文件,这让它可以做一些事情,而不仅仅是从头到尾读取文件。一些例子:
它可以从文件中乱序读取。如果您使用
cat hugefile | tail
,tail
程序必须读取整个文件才能到达末尾,但如果您使用tail <hugefile
,它可以lseek()
跳到文件末尾附近,然后向后读取,直到获得所需的内容。它只能读取文件的一部分而不会造成麻烦。如果您使用
head <hugefile
,head
将读取它需要的内容,然后退出(这会关闭文件);没有混乱,没有大惊小怪。但如果您使用cat hugefile | head
,当head
退出并关闭管道时,cat
仍会尝试将数据推送到现在无目的地的管道中。为了解决这个问题,系统将SIGPIPE
信号发送到cat
。许多程序在获取时会打印一条错误消息SIGPIPE
;我测试过的版本cat
没有这样做,但它们确实以错误状态退出。如果这是在设置-e
和模式的脚本中pipefail
(如“非官方 Bash 严格模式”),bash 会将其视为致命错误并退出脚本。 (这种事我不推荐set -e
。)它可以访问文件的属性。如果您使用
pv <hugefile | slowprocessor
,pv
(“管道查看器”实用程序)将检查文件的大小,并为您提供一个进度条,显示到目前为止已发送的文件百分比以及预计完成时间。但如果您使用cat hugefile | pv | slowprocessor
,pv
将不知道文件有多大,只显示已发送的绝对数量。 (注意:pv
确实有一个-s
选项可以让您明确告诉它您认为文件有多大。)
因此总体而言,直接访问输入文件使程序在使用文件的方式上具有更大的灵活性。
此外,许多程序(包括我在这里用作示例的所有程序)允许您直接将输入文件指定为命令参数(例如,tail hugefile
而不是tail <hugefile
或cat hugefile | tail
)。这允许程序更信息并控制如何访问文件。它还(同样,对于支持此功能的命令)允许多个输入文件(就像cat |
那样)。因此,对于支持它的命令,它通常优于管道或输入重定向。