另一个主要区别是返回值/退出代码的传播(我将使用更简单的命令来说明):
管道:
$ ls -l /notthere | tee listing.txt
ls: cannot access '/notthere': No such file or directory
$ echo $?
0
-> tee的退出码被传播
进程替换:
$ ls -l /notthere > >(tee listing.txt)
ls: cannot access '/notthere': No such file or directory
$ echo $?
2
-> ls
的退出代码被传递
当然,有几种方法可以解决这个问题(例如set -o pipefail
、变量PIPESTATUS
),但我认为值得一提的是,这是默认行为。
另一个相当微妙但可能令人烦恼的区别在于子进程终止(最好使用产生大量输出的命令进行说明):
管道:
#!/usr/bin/env bash
tar --create --file /tmp/etc-backup.tar --verbose --directory /etc . 2>&1 | tee /tmp/etc-backup.log
retval=${PIPESTATUS[0]}
(( ${retval} == 0 )) && echo -e "\n*** SUCCESS ***\n" || echo -e "\n*** FAILURE (EXIT CODE: ${retval}) ***\n"
-> 在包含管道结构的行之后,所有管道命令已经终止(否则PIPESTATUS
无法包含它们各自的退出代码)
进程替换:
#!/usr/bin/env bash
tar --create --file /tmp/etc-backup.tar --verbose --directory /etc . &> >(tee /tmp/etc-backup.log)
retval=$?
(( ${retval} == 0 )) && echo -e "\n*** SUCCESS ***\n" || echo -e "\n*** FAILURE (EXIT CODE: ${retval}) ***\n"
-> 在进程替换所在的行之后,即在>(...)
内部的命令,在本例中是tee
,可能仍在运行,可能会导致不同步的控制台输出(成功/失败消息与仍在流动的tar
输出混合)。[*]
[*] 可以在帧缓冲控制台上重现,但似乎不会影响像KDE的Konsole这样的GUI终端(可能是由于不同的缓冲策略)。
> filename.ext
或< filename.ext
,但是它是一个进程而不是一个文件? - Ashton Wiersdorfecho <(date)
和cat <(date)
给出了这个如何工作的良好线索。 - that other guyecho <(date)
示例应该可以说明这一点,因为它只是打印文件名而不是从文件中读取内容。 - Gordon Davisson