3

我似乎无法弄清楚,这里发生了什么。我有 4 个版本的相同代码,唯一的区别是代码块/行的顺序。我最初的期望是重定向顺序没有任何区别,但它似乎并不正确。我还假设 >() 具有文件描述符机制的一些屏蔽属性,但没有...

请不要问我用它做什么,我也不需要替代解决方案,我想了解这段代码。或者我对过程替代的理解的信念将永远被打破......

元代码:

1_cmd_producing_both_stdout_and_stderr
|
+-stdout-> 2_cmd_producing_both_stdout_and_stderr
|          |
|          +-stdout-> A_awk_writing_stdout_to_file_producing_stderr
|          |
|          +-stderr-> B_awk_writing_stdout_to_file_producing_stderr
|
+-stdout-> 3_cmd_producing_both_stdout_and_stderr
           |
           +-stdout-> C_awk_writing_stdout_to_file_producing_stderr
           |
           +-stderr-> D_awk_writing_stdout_to_file_producing_stderr

版本 1: 1 2 AB 3 CD

版本 2: 1 2 BA 3 DC

版本 3: 1 3 DC 2 BA

版本 4: 1 3 CD 2 AB

笔记:

我也尝试了 2 AB 和 2 BA,它们产生一致的输出,类似于版本 1。

awk:GNU awk 4.1.1,API:1.1(GNU MPFR 3.1.2-p3,GNU MP 6.0.0)

bash:GNU bash,版本 4.3.30(1)-release (x86_64-pc-linux-gnu)

版本 1,这会产生我期望的输出:

( echo log; echo err 1>&2; ) \
    1> >( ( echo -n '1.'; cat; echo '1.ERR' 1>&2 ; ) \
        1> >( awk 'BEGIN { print "error 1" >"/dev/stderr" } { print $0 }' >out.out ) \
        2> >( awk 'BEGIN { print "error 2" >"/dev/stderr" } { print $0 }' >out.err )
    ) \
    2> >( ( echo -n '2.'; cat; echo '2.ERR' 1>&2 ; ) \
        1> >( awk 'BEGIN { print "error 3" >"/dev/stderr" } { print $0 }' >err.out ) \
        2> >( awk 'BEGIN { print "error 4" >"/dev/stderr" } { print $0 }' >err.err )
    )

文件内容:

out.out 1.log
out.err 1.ERR
err.out 2.err
err.err 2.ERR

输出:

error 4
error 2
error 1
error 3

版本 2:

注意:与版本 1 相比,第 1 与第 2、第 3 与第 4 二级缩进行交换。

( echo log; echo err 1>&2; ) \
    1> >( ( echo -n '1.'; cat; echo '1.ERR' 1>&2 ; ) \
        2> >( awk 'BEGIN { print "error 1" >"/dev/stderr" } { print $0 }' >out.err ) \
        1> >( awk 'BEGIN { print "error 2" >"/dev/stderr" } { print $0 }' >out.out )
    ) \
    2> >( ( echo -n '2.'; cat; echo '2.ERR' 1>&2 ; ) \
        2> >( awk 'BEGIN { print "error 3" >"/dev/stderr" } { print $0 }' >err.err ) \
        1> >( awk 'BEGIN { print "error 4" >"/dev/stderr" } { print $0 }' >err.out )
    )

文件内容:

(!) out.err error 2\n1.ERR
out.out 1.log
(!) err.err 2.ERR\nerror 4
err.out 2.err

输出:

error 3
error 1

版本 3:

注意:与版本 2 相比,交换了第一级缩进代码块。

( echo log; echo err 1>&2; ) \
    2> >( ( echo -n '2.'; cat; echo '2.ERR' 1>&2 ; ) \
        2> >( awk 'BEGIN { print "error 1" >"/dev/stderr" } { print $0 }' >err.err ) \
        1> >( awk 'BEGIN { print "error 2" >"/dev/stderr" } { print $0 }' >err.out )
    ) \
    1> >( ( echo -n '1.'; cat; echo '1.ERR' 1>&2 ; ) \
        2> >( awk 'BEGIN { print "error 3" >"/dev/stderr" } { print $0 }' >out.err ) \
        1> >( awk 'BEGIN { print "error 4" >"/dev/stderr" } { print $0 }' >out.out )
    )

文件内容:

(!) err.err error 2\n2.ERR
(!) err.out 2.err\nerror 3
(!) out.err 1.ERR\nerror 4
out.out 1.log

输出:

error 1
(!)

版本 4:

注意:与版本 3 相比,第 1 与第 2、第 3 与第 4 二级缩进行交换。

( echo log; echo err 1>&2; ) \
    2> >( ( echo -n '2.'; cat; echo '2.ERR' 1>&2 ; ) \
        1> >( awk 'BEGIN { print "error 1" >"/dev/stderr" } { print $0 }' >err.out ) \
        2> >( awk 'BEGIN { print "error 2" >"/dev/stderr" } { print $0 }' >err.err )
    ) \
    1> >( ( echo -n '1.'; cat; echo '1.ERR' 1>&2 ; ) \
        1> >( awk 'BEGIN { print "error 3" >"/dev/stderr" } { print $0 }' >out.out ) \
        2> >( awk 'BEGIN { print "error 4" >"/dev/stderr" } { print $0 }' >out.err )
    )

文件内容:

(!) err.out 2.err\nerror 4\nerror 3
err.err 2.ERR
out.out 1.log
out.err 1.ERR

输出:

error 2
error 1
(!)

版本 2、3、4 中发生了什么?

4

1 回答 1

0

您认为输出重定向的顺序无关紧要的假设是不正确的。订单很重要。让我们考虑一下代码示例 2 中的字符串“error 2”,看看它为什么会被写入文件out.err。代码是:

( echo log; echo err 1>&2; ) \
    1> >( ( echo -n '1.'; cat; echo '1.ERR' 1>&2 ; ) \
        2> >( awk 'BEGIN { print "error 1" >"/dev/stderr" } { print $0 }' >out.err ) \
        1> >( awk 'BEGIN { print "error 2" >"/dev/stderr" } { print $0 }' >out.out )
    ) \
    2> >( ( echo -n '2.'; cat; echo '2.ERR' 1>&2 ; ) \
        2> >( awk 'BEGIN { print "error 3" >"/dev/stderr" } { print $0 }' >err.err ) \
        1> >( awk 'BEGIN { print "error 4" >"/dev/stderr" } { print $0 }' >err.out )
)

进程 B 将字符串“error 2”写入其标准错误。由于 shell 解析以启动进程 B 的字符串不包含到 stderr 的重定向,因此进程 B 从其父进程继承其 stderr。它的父级是您标记的子shell 2。该进程有 2 个重定向,每个重定向到子进程。在进程 B 启动时,进程 2 的 stderr 已被定向到进程 A,因此进程 B 将在此处写入字符串。进程 A 读取它并将字符串写入其标准输出,即文件out.err。如果重定向的顺序相反,则进程的stderr2尚未被重定向,因此进程B的stderr将与原始进程的stderr相同(例如,您的tty)

于 2017-07-10T14:03:50.207 回答