寻找shell重定向交织行为的解释

时间:2014-06-03 02:21:49

标签: bash shell

给出以下脚本(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

我确定这种行为有理由我不知道它是什么。有人能够启发我吗?

1 个答案:

答案 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