如何安全地“组合”由多个程序打印的行?


11

假设我要并行执行多个程序,并将它们的输出组合到一个管道中:

sh -c '
    (echo qqq; echo qqq2; echo qqq3)&
    (echo www; echo www2; echo www3)& 
    (echo eee; echo eee2; echo eee3)& 
  wait; wait; wait'

这种shell方法在这种简单情况下效果很好,但我希望如果程序以缓冲方式输出越来越长的行,则它会失败,例如:

qqq
qqwww
q2
qqq3www2

wwweee3

eee2
eee3

我被提示使用的解决方案之一是tail -f

tail -n +0 -q -f <(echo qqq; echo qqq2; echo qqq3) <(echo www; echo www2; echo www3) <(echo eee; echo eee2; echo eee3)

,但这是次优的选择:输出数据缓慢,不会终止;在这种情况下,我看到的输出不是按“睡眠”顺序,而是按参数顺序:

tail -n +0 -q -f <(sleep 1; echo qqq; sleep 1; echo qqq2; echo qqq3) <(echo www; echo www2; sleep 10; echo www3) <(echo eee; sleep 4; echo eee2; echo eee3) | cat

我为此实现了一个特殊的小程序,但是相信应该有一些标准的好方法。

如何使用标准工具(没有tail -f缺点)进行操作?


您想如何混合输出?显然,您确实想混合输出,因为您需要“睡眠顺序”而不是“参数顺序”。您是否需要混合输出而不是混合行,即以原子方式打印每行?
吉尔(Gilles)'所以

逐行。所有启动程序中的所有行都应尽早交付,但不要在每行内部混用。
六。

我认为这样做的标准方法是syslog……
Shadur

是否syslog不将日志用于日志,而是将其用于自定义事务?
六。

到目前为止,这并不比其他建议更理想,但我认为值得一提的-s是tail选项。例如,tail -f -s .1 file将循环延迟从默认的1秒减少到0.1秒。
cpugeniusmv

Answers:


3

GNU并行。

从日期为2013年8月的发行说明中:

--line-buffer将基于行缓冲输出。--group将整个工作的输出保持在一起。--ungroup允许输出混合在一起,其中一半来自一个作业,一半来自另一份作业。--line-buffer介于两者之间;它会打印一条整行,但允许混合不同作业的行。

例如:

parallel --line-buffer <jobs

其中jobs包含:

./long.sh
./short.sh one
./short.sh two

short.sh

#!/bin/bash

while true; do
        echo "short line $1"
        sleep .1
done

long.sh

#!/bin/bash

count=0
while true; do
        echo -n "long line with multiple write()s "
        sleep .1
        count=$((count+1))
        if [ $count -gt 30 ]; then
                count=0
                echo
        fi
done

输出:

short line one
short line two
short line one
short line two
short line one
**-snip-**
short line one
short line one
short line two
short line two
short line one
short line one
short line one
long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s 
short line two
short line two
short line two
short line one

1

一个实现锁的解决方案:

function putlines () {
   read line || return $?
   while ! ln -s $$ lock >/dev/null 2>&1
   do
      sleep 0.05
   done
   echo "$line" 
}

function getlines () {
     while read lline
     do 
          echo "$lline"
          rm lock
     done
}

# your paralelized jobs  
(  
   job1 | putlines & 
   job2 | putlines & 
   job3 | putlines & 
   wait
) | getlines| final_processing

创建锁的方法应该比使用文件系统更快。


0

我想不到有什么简单的方法可以对您有所帮助,如果您的行太长,那么一个程序将在它能够完成之前将其发送到睡眠状态,以完成向stdout的写行。

但是,如果您的行足够短以至于无法在过程切换之前完全写入,那么您的问题是,生成一行会花费很长时间,则可以使用read缓冲输出。

例如:

((./script1 | while read line1; do echo $line1; done) & \
(./script2 | while read line2; do echo $line2; done)) | doSomethingWithOutput

不美丽的。不太可能可靠。这种性能可能不太好。
六。

你是对的。它不是很漂亮,但看起来更像是一个肮脏的hack。但是,我认为这还不足以判断性能和可靠性。另外,您想使用“标准工具”。因此,如果您不得不接受一些丑陋的话(最后),我不会感到惊讶。但也许有人有一个更令人满意的解决方案。
xwst

目前,我对我的程序(问题中的链接)感到满意,只是它在存储库中不可用,因此甚至不能被视为一点“标准”。解决方案可能是尝试将其推到那里...
Vi。

0

您可以使用创建一个命名管道mkfifo,将所有输出转储到命名管道中,并分别从命名管道中读取收集的数据:

mkfifo /tmp/mypipe
job1 > /tmp/mypipe &
job2 > /tmp/mypipe &
job3 > /tmp/mypipe &

cat /tmp/mypipe > /path/to/final_output &

wait; wait; wait; wait

1
这将如何保护您免于何时出现混乱job1job2输出长(> 4096字节)行?这似乎与问题中第一个示例的等效管道命名为管道。
六。

很公平的一点。尽管您的问题中明确指出了大输出,但我并未考虑它。我现在想知道是否没有某种工具可以执行的相反操作tee,听起来像您想要的一样。可能要看一下syslog或其他日志记录工具的内部,因为它们肯定会将多个位置的输出聚合到一个日志文件中。就像@emmanual所建议的那样,锁定很可能是正确的答案。
DopeGhoti
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.