给出以下脚本(t.sh):
#!/bin/bash
if [ $# -eq 0 ]; then
log() {
{
if [ $# -gt 0 ]; then
printf -- %s\\n "$*"
else
cat
fi
} | tee -a logged.out
}
else
log() {
if [ $# -gt 0 ]; then
printf -- %s\\n "$*"
else
cat
fi
} > >(tee -a logged.out)
fi
declare -xf log
: > logged.out
./t2.sh 2>&1 | tee verbose.out
其中t2.sh是:
#!/bin/bash
echo outone
echo errone >&2
echo logged pipe | log
echo outtwo
echo errtwo >&2
log logged message
echo outthree
echo errthree >&2
我没有理解两个版本的日志功能之间输出的差异。
默认(第一个)函数执行我想要的操作,它在将记录的输出发送到logged.out时正确地交错了verbose.out文件中的stdout,stderr和日志函数输出。
然而,第二个函数没有正确地将记录的输出与stdout和stderr输出交错,而是似乎缓冲了记录的输出,因此它最终在verbose.out文件的末尾(尽管这个输出顺序不是甚至是一致的,并且记录的消息偶尔会以相反的顺序出现在输出中,并且第一条消息可以在输出的某个地方更早出现。)
正确操作:
$ ./t.sh; more {logged,verbose}.out | cat
outone
errone
logged pipe
outtwo
errtwo
logged message
outthree
errthree
::::::::::::::
logged.out
::::::::::::::
logged pipe
logged message
::::::::::::::
verbose.out
::::::::::::::
outone
errone
logged pipe
outtwo
errtwo
logged message
outthree
errthree
操作不正确:
$ ./t.sh broken; more {logged,verbose}.out | cat
outone
errone
outtwo
errtwo
outthree
errthree
logged message
logged pipe
::::::::::::::
logged.out
::::::::::::::
logged message
logged pipe
::::::::::::::
verbose.out
::::::::::::::
outone
errone
outtwo
errtwo
outthree
errthree
logged message
logged pipe
我确定这种行为有理由我不知道它是什么。有人能够启发我吗?
答案 0 :(得分:4)
这是一个更简单的测试用例。为什么
echo foo | cat
echo bar
打印foo
后跟bar
,而
echo foo > >(cat)
echo bar
首先使用bar
以相反的顺序打印它们?
这是因为对于管道,bash等待管道中的所有阶段完成。对于进程替换,该进程只是分叉而不是等待。
这意味着在下一个语句执行之前,进程替换命令是否可以启动,读取和处理正在写入的内容存在竞争条件,并且在您的情况下它正在丢失。
通过简单的实验,您可以更清楚地看到此效果:
echo foo | sleep 10 # Waits 10 seconds
echo foo > >(sleep 10) # Finishes immediately