Wie kann man Zeilen, die von mehreren Programmen gedruckt wurden, sicher „kombinieren“?


11

Angenommen, ich möchte mehrere Programme parallel ausführen und ihre Ausgaben zu einer Pipe kombinieren:

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

Dieser Shell-Ansatz funktioniert in diesem einfachen Fall gut, aber ich erwarte, dass er fehlschlägt, wenn Programme mehr und längere Zeilen gepuffert ausgeben, wie folgt (konstruiert):

qqq
qqwww
q2
qqq3www2

wwweee3

eee2
eee3

Eine der Lösungen, die ich verwenden sollte, war 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)

Dies ist jedoch eine nicht optimale Option: Sie gibt Daten nur schleppend aus und wird nicht beendet. Ich sehe die Ausgaben nicht in der Reihenfolge "Schlaf", sondern in der Reihenfolge der Argumente in diesem Fall:

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

Ich habe ein spezielles kleines Programm dafür implementiert , glaube aber, dass es einen Standard-guten Weg geben sollte, dies zu tun.

Wie geht das mit Standardwerkzeugen (und ohne tail -fNachteil)?


Wie möchten Sie die Ausgabe mischen? Anscheinend möchten Sie die Ausgabe mischen, da Sie "Schlafreihenfolge" anstelle von "Argumentreihenfolge" möchten. Müssen Sie die Ausgabe mischen, aber nicht die Zeilen, dh jede Zeile atomar drucken lassen?
Gilles 'SO - hör auf böse zu sein'

Linienweise. Alle Zeilen aus allen gestarteten Programmen sollten frühzeitig geliefert werden, ohne jedoch in jeder Zeile zu mischen.
Vi.

Ich denke, die Standardmethode dafür heißt syslog...
Shadur

Wird die Verwendung syslognicht für Protokolle, sondern für etwas Benutzerdefiniertes als OK angesehen?
Vi.

Dies ist nicht idealer als andere Vorschläge, die bisher veröffentlicht wurden, aber ich dachte, es wäre erwähnenswert, die -sOption für den Schwanz zu erwähnen . zB tail -f -s .1 filereduziert die Schleifenverzögerung von 1 Sekunde auf 0,1 Sekunden.
cpugeniusmv

Antworten:


3

GNU Parallel.

Aus den Versionshinweisen vom August 2013:

--line-bufferpuffert die Ausgabe auf Zeilenbasis. --grouphält die Ausgabe für einen ganzen Job zusammen. --ungroupErmöglicht die Verwechslung der Ausgabe mit einer halben Zeile aus einem Job und einer halben Zeile aus einem anderen Job. --line-bufferpasst zwischen diese beiden; Es wird eine vollständige Zeile gedruckt, es können jedoch Zeilen verschiedener Jobs gemischt werden.

Beispielsweise:

parallel --line-buffer <jobs

Wo jobsenthält:

./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

Ausgabe:

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

Eine Lösung, die Sperren implementiert:

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

Es sollte einen schnelleren Weg geben, eine Sperre zu erstellen, als das Dateisystem zu verwenden.


0

Ich kann mir nichts Einfaches vorstellen, das Ihnen helfen könnte, wenn Ihre Zeilen so lang sind, dass ein Programm in den Ruhezustand versetzt wird, bevor es in der Lage war, eine Zeile an stdout zu schreiben.

Wenn Ihre Zeilen jedoch kurz genug sind, um vor dem Prozesswechsel vollständig geschrieben zu werden, und Ihr Problem darin besteht, dass das Generieren einer Zeile sehr lange dauert, können Sie die Ausgabe mit read puffern.

Z.B:

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

Nicht schön. Unwahrscheinlich so zuverlässig. Es ist unwahrscheinlich, dass die Leistung gut sein wird.
Vi.

Du hast recht. Es ist nicht schön, sieht aber eher wie ein schmutziger Hack aus. Ich denke jedoch nicht, dass dies ausreicht, um Leistung und Zuverlässigkeit zu beurteilen. Außerdem wollten Sie 'Standardwerkzeuge' verwenden. Ich wäre also nicht überrascht, wenn Sie (am Ende) etwas Hässliches akzeptieren müssten. Aber vielleicht hat jemand eine zufriedenstellendere Lösung.
xwst

Derzeit bin ich mit meinem Programm (in der Frage verlinkt) zufrieden, außer dass es nicht in Repositories verfügbar ist und daher nicht einmal als kleiner "Standard" betrachtet werden kann. Die Lösung könnte darin bestehen, zu versuchen, es dorthin zu schieben ...
Vi.

0

Sie können eine Named Pipe mit mkfifoerstellen, die gesamte Ausgabe in die Named Pipe sichern und für Ihre gesammelten Daten separat von der Named Pipe lesen:

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

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

wait; wait; wait; wait

1
Wie wird dieser Schutz von Mangeln , wenn job1und job2Ausgang lang (> 4096 Bytes) Linien? Dies scheint als Pipe-Äquivalent des ersten Code-Beispiels in der Frage bezeichnet zu werden.
Vi.

Sehr fairer Punkt. Ich habe die Ausgabe mit großen Blobs nicht berücksichtigt, obwohl dies in Ihrer Frage ausdrücklich erwähnt wurde. Ich frage mich jetzt, ob es vielleicht kein Tool gibt, das das Gegenteil bewirkt tee, was genau so klingt, wie Sie es möchten. Schauen Sie sich möglicherweise die Interna syslogoder andere Protokollierungswerkzeuge an, da diese die Ausgabe von mehreren Stellen definitiv in einer Protokolldatei zusammenfassen. Das Sperren kann durchaus die richtige Antwort sein, wie auch von @emmanual vorgeschlagen.
DopeGhoti
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.