当两个子 shell 写入 stdout 时对输出进行排序

当两个子 shell 写入 stdout 时对输出进行排序

我有以下形式的命令

input | tee >(subshell) | mainshell

子 shell 和主 shell 都写入标准输出。所以他们的输出不同步。例如

echo "Hello\nWorld" | tee >(grep -o ell | tr 'a-z' 'A-Z') | grep orld | sed 's/orl/ORL/g'

上面的命令打印

ELL
WORLd

有时和

WORLd
ELL

其他时间。

有没有一种简单的方法可以确保它们按预期顺序排列,而不必使用临时文件/命名的 fifo?可能打开一些文件描述符重定向会有帮助?

更新:

按顺序,我指的是主 shell 的输出,然后是子 shell 的输出(反之亦然)。如果它是确定性的,我可以将它们交换到我的需要。

同样可以通过命名 fifo 来实现,如下所示

mkfifo f1 f2
echo "Hello\nWorld" | tee >(grep -o ell | tr 'a-z' 'A-Z' > f1) | grep orld | sed 's/orl/ORL/g' > f2 &
cat f1 f2
rm f1 f2

我想知道是否可以避免临时 fifo 或文件。

答案1

有没有一种简单的方法可以确保它们按预期顺序排列,而不必使用临时文件/命名的 fifo?可能打开一些文件描述符重定向会有帮助?

并不真地。和>(...)...|...不是边界保留——不能保证单次写入不会在另一端变成多次读取(反之亦然)。

为了强制执行某些顺序,您可以使用建议锁定——查看flock(1)flock(2)联机帮助页。如何实现这一点很大程度上取决于subshell程序mainshell的工作方式。盲目锁定非协作程序很容易导致死锁(如果它有效的话)。

答案2

GNU Parallel--tee可以--keep-order强制顺序保持不变:

ell() {
  grep -o ell | tr 'a-z' 'A-Z'
}
orld() {
  grep orld | sed 's/orl/ORL/g'
}
export -f ell
export -f orld

echo "Hello\nWorld" | parallel -k --tee --pipe ::: ell orld

然而,它确实在幕后使用 fifo 和临时文件。但如果您的目标只是不想自己处理这个问题,那么这可能是一个解决方案。

该解决方案使用 tmpfiles,但它们几乎立即取消链接:

myfunc1() { grep -o ell | tr 'a-z' 'A-Z'; }
myfunc2() { grep orld | sed 's/orl/ORL/g'; }

touch tmp1 tmp2
(
    echo "Hello\nWorld" |
        tee >( (rm tmp1; myfunc1) > tmp1 ) |
        (rm tmp2; myfunc2) > tmp2
    cat <&3
    cat <&4
) 3< tmp1 4< tmp2

或者更短:

(
    rm tmp1 tmp2
    echo "Hello\nWorld" |
        tee >( grep -o ell | tr 'a-z' 'A-Z' >&5 ) |
        grep orld | sed 's/orl/ORL/g' >&6
    cat <&3
    cat <&4
) 5> tmp1 6> tmp2 3< tmp1 4< tmp2

相关内容