複数のプログラムによって印刷された行を安全に「組み合わせる」方法は?


11

複数のプログラムを並行して実行し、それらの出力を1つのパイプに結合したいとします。

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

このシェルアプローチは、この単純なケースではうまく機能しますが、プログラムが次のように、バッファされた方法でより多くの長い行を出力する場合は失敗すると予想されます(構成されています)。

qqq
qqwww
q2
qqq3www2

wwweee3

eee2
eee3

私が使用するように示唆された解決策の1つは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「SO-邪悪なことをやめなさい」

ラインワイズ。開始されたすべてのプログラムからのすべてのラインは、各ライン内で混合することなく、早期に配信される必要があります。
Vi。

これを行うための標準的な方法は、よく呼ばれていると思いますsyslog
Shadur '28

syslogログではなく、カスタムで問題ないと思われるものを使用していますか?
Vi。

これはこれまでに投稿された他の提案よりも理想的ではありませんが-s、尾のオプションについて言及する価値があると思いました。たとえばtail -f -s .1 file、ループ遅延をデフォルトの1秒から0.1秒に減らします。
cpugeniusmv 2013年

回答:


3

GNU Parallel。

2013年8月付けのリリースノートから:

--line-buffer行ごとに出力をバッファリングします。--groupジョブ全体で出力をまとめます。--ungroup出力を、あるジョブからの行の半分と別のジョブからの行の半分と混在させることができます。--line-bufferこれらの2つの間に収まります。それは完全な行を印刷しますが、異なるジョブの行を混在させることができます。

例えば:

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

行が非常に長い場合に、1つのプログラムが実行可能になる前にスリープ状態になり、標準出力への行の書き込みを完了するのに役立つ単純なものは考えられません。

ただし、プロセスの切り替え前に完全に書き込むことができるほど短い行で、1つの行の生成に非常に長い時間がかかるという問題がある場合は、readを使用して出力をバッファリングできます。

例えば:

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

美しくない。信頼性は低いです。おそらくそのパフォーマンスは良いでしょう。
Vi。

あなたが正しい。美しくはありませんが、汚いハックのように見えます。しかし、パフォーマンスと信頼性を判断するにはそれだけでは不十分だと思います。さらに、「標準ツール」を使用したいと考えていました。したがって、(最終的に)醜さを受け入れる必要がある場合でも、私は驚かないでしょう。しかし、多分誰かがより満足のいく解決策を持っています。
xwst 2013年

現在、私は自分のプログラム(質問にリンクされています)に満足しています。ただし、リポジトリで使用できないため、少しでも「標準」とは見なされません。解決策はそれをそこに押し込もうとすることかもしれません...
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
これは、長い(> 4096バイト)行job1job2出力するときのマングリングからどのように保護しますか?これは、質問の最初のコード例に相当する名前付きパイプのようです。
Vi。

非常に公平なポイント。あなたの質問で明示的に呼び出されているにもかかわらず、私はラージブロブ出力を考慮しませんでした。私は今、おそらくteeあなたが望んでいるように聞こえる、の逆を行ういくつかのツールがないのだろうかと思っています。syslog複数の場所からの出力を1つのログファイルに確実に集約するため、またはその他のロギングツールの内部を調べてください。@emmanualが示唆しているように、ロックも正解かもしれません。
DopeGhoti 2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.