代理 stdout/stderr 在 bash 中保持秩序

Proxy stdout/stderr keeping order in bash

我正在尝试在文件输出的每一行前面添加一些信息,方法是:

这是我的测试脚本:

#!/bin/bash
#
# Test proxying stdout and stderr
#

function proxy-stdouterr() {
    local name=""
    local handle=
    while IFS='' read -r line
    do
        echo -e "[ ${name}: ${line} ]" >&${handle}
    done
}

# Output some messages and attempt to parse them
(
    echo "1: Normal message"
    echo "2: Error" >&2
    echo "3: Normal message"
    echo "4: Error" >&2
) 2> >(proxy-stdouterr "stderr" 2) > >(proxy-stdouterr "stdout" 1)

这工作得很好,但不保留终端中的顺序 (Ubuntu 12.04)。

不代理输出时:

1: Normal message
2: Error
3: Normal message
4: Error

但是当代理时订单不被保留。更糟糕的是,这不是确定性的,大多数时候是:

[ stderr: 2: Error ]
[ stderr: 4: Error ]
[ stdout: 1: Normal message ]
[ stdout: 3: Normal message ]

但偶尔:

[ stderr: 2: Error ]
[ stdout: 1: Normal message ]
[ stderr: 4: Error ]
[ stdout: 3: Normal message ]

我该如何解决这个问题?

谢谢

正如其他人所指出的,这是一个同步问题,没有明显的 在不知道更多关于您具体控制哪些部分的情况下回答 想要改变。

恕我直言,主要有两个攻击面:

  • 流缓冲。大多数 C 派生程序不缓冲 stderr, 如果它们是终端和缓冲区,则逐行缓冲标准输入和标准输出 他们完全不同。这解释了为什么 stderr 总是出现 在你的例子中首先出现。一个廉价的应对方法是使用 /usr/bin/echo 而不是内置 echo,所以你会在最后得到一个刷新的流 每一行(以及进程生成的大量延迟)。它解决了 你的例子 100% 是我试过的,所以对你来说可能就足够了 也。如果您可以控制生成这些行的命令, 确保 stdout 和 stderr 都是行缓冲的。

  • 进程调度。你有两个进程竞争读取 两个流。如果当时两者都有数据流动, 他们在互相竞争。您可以通过以下方式减轻这种情况 让一个进程来做,但我不确定是否有可能 在原生 bash 中保持高效(不可编程 POSIX select 最终用户可用)

无论如何,如果图片中的任何地方都在排队,你就是 就保持订单而言,搞砸了。