我在
tee
信息页面中遇到了使用 tee
实用程序的示例:
wget -O - http://example.com/dvd.iso | tee >(sha1sum > dvd.sha1) > dvd.iso
我查找了
>(...)
语法,发现了一种叫做“进程替换”的东西。据我了解,它使一个进程看起来像一个文件,另一个进程可以将其输出写入/附加到其中。 (如果我在这一点上有错,请纠正我。)
这与管道有何不同? (
|
)我看到上面的例子中使用了管道——这只是一个优先级问题吗?还是还有其他区别?
这里没有任何好处,因为该行同样可以写成这样:
wget -O - http://example.com/dvd.iso | tee dvd.iso | sha1sum > dvd.sha1
当您需要与多个程序进行管道传输时,差异就开始出现,因为这些不能纯粹用
|
来表达。欢迎尝试:
# Calculate 2+ checksums while also writing the file
wget -O - http://example.com/dvd.iso | tee >(sha1sum > dvd.sha1) >(md5sum > dvd.md5) > dvd.iso
# Accept input from two 'sort' processes at the same time
comm -12 <(sort file1) <(sort file2)
它们在某些情况下也很有用,在这种情况下,您出于某种原因不能或不想使用管道:
# Start logging all error messages to file as well as disk
# Pipes don't work because bash doesn't support it in this context
exec 2> >(tee log.txt)
ls doesntexist
# Sum a column of numbers
# Pipes don't work because they create a subshell
sum=0
while IFS= read -r num; do (( sum+=num )); done < <(curl http://example.com/list.txt)
echo "$sum"
# apt-get something with a generated config file
# Pipes don't work because we want stdin available for user input
apt-get install -c <(sed -e "s/%USER%/$USER/g" template.conf) mysql-server
一个主要区别是返回值/退出代码的传播(我将使用更简单的命令来说明):
管道:
$ 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 终端(可能是由于不同的缓冲策略)。