Wenn ich einen Befehl aufrufe, echo
kann ich beispielsweise die Ergebnisse dieses Befehls in mehreren anderen Befehlen mit verwenden tee
. Beispiel:
echo "Hello world!" | tee >(command1) >(command2) >(command3)
Mit cat kann ich die Ergebnisse mehrerer Befehle sammeln. Beispiel:
cat <(command1) <(command2) <(command3)
Ich möchte in der Lage sein, beide Dinge gleichzeitig zu tun, damit ich tee
diese Befehle auf der Ausgabe von etwas anderem (zum Beispiel dem, das echo
ich geschrieben habe) aufrufen und dann alle ihre Ergebnisse auf einer einzigen Ausgabe mit sammeln kann cat
.
Es ist wichtig , die Ergebnisse zu halten , um, bedeutet dies die Linien in der Ausgabe command1
, command2
und command3
sollte nicht miteinander verflochten werden, aber bestellt als die Befehle sind (wie es mit geschieht cat
).
Es gibt vielleicht bessere Optionen als cat
und tee
aber das sind die, die ich bisher kenne.
Ich möchte die Verwendung temporärer Dateien vermeiden, da die Ein- und Ausgabe möglicherweise sehr umfangreich ist.
Wie könnte ich das machen?
PD: Ein weiteres Problem besteht darin, dass dies in einer Schleife geschieht, was den Umgang mit temporären Dateien erschwert. Dies ist der aktuelle Code, den ich habe, und er funktioniert für kleine Testfälle, aber er erzeugt Endlosschleifen beim Lesen und Schreiben aus der Aux-Datei, wie ich es nicht verstehe.
somefunction()
{
if [ $1 -eq 1 ]
then
echo "Hello world!"
else
somefunction $(( $1 - 1 )) > auxfile
cat <(command1 < auxfile) \
<(command2 < auxfile) \
<(command3 < auxfile)
fi
}
Lesen und Schreiben in Auxfile scheinen sich zu überschneiden und alles explodieren zu lassen.
quelle
echo HelloWorld > file; (command1<file;command2<file;command3<file)
oder für die Ausgabeecho | tee cmd1 cmd2 cmd3; cat cmd1-output cmd2-output cmd3-output
. Genau so funktioniert es - tee kann Eingaben nur aufteilen, wenn alle Befehle parallel arbeiten und verarbeitet werden. Wenn ein Befehl inaktiv ist (weil Sie keine Verschachtelung wünschen), werden einfach alle Befehle blockiert, um zu verhindern, dass der Speicher mit EingabenAntworten:
Sie könnten eine Kombination von GNU STDBUF und verwenden
pee
von moreutils :Pee
popen(3)
s diese 3 Shell-Kommandozeilen und dannfread
s die Eingabe undfwrite
s alles drei, die bis zu 1M gepuffert werden.Die Idee ist, einen Puffer zu haben, der mindestens so groß ist wie die Eingabe. Auf diese Weise werden die drei Befehle zwar gleichzeitig gestartet, sie sehen jedoch nur Eingaben, wenn
pee
pclose
die drei Befehle nacheinander ausgeführt werden.Bei jedem
pclose
,pee
leert den Puffer auf den Befehl und wartet auf seine Beendigung.cmdx
Dies stellt sicher, dass die Ausgabe der drei Befehle nicht erfolgt, solange diese Befehle noch keine Eingaben empfangen haben (und keinen Prozess auslösen, der nach der Rückkehr der übergeordneten Befehle möglicherweise fortgesetzt wird) verschachtelt.In der Tat ist das ein bisschen wie das Verwenden einer temporären Datei im Speicher, mit dem Nachteil, dass die 3 Befehle gleichzeitig gestartet werden.
Um zu vermeiden, dass die Befehle gleichzeitig gestartet werden, können Sie
pee
als Shell-Funktion schreiben :Achten Sie jedoch darauf, dass
zsh
bei der Binäreingabe mit NUL-Zeichen andere Shells als fehlschlagen.Das vermeidet die Verwendung temporärer Dateien, aber das bedeutet, dass die gesamte Eingabe im Speicher gespeichert wird.
In jedem Fall müssen Sie die Eingabe irgendwo im Speicher oder in einer temporären Datei speichern.
Eigentlich ist es eine interessante Frage, da sie uns die Grenzen der Unix-Idee aufzeigt, mehrere einfache Tools für eine einzige Aufgabe zusammenarbeiten zu lassen.
Hier möchten wir, dass mehrere Tools für die Aufgabe zusammenarbeiten:
echo
)tee
)cmd1
,cmd2
,cmd3
)cat
).Es wäre schön, wenn sie alle zur gleichen Zeit laufen und hart an den Daten arbeiten könnten, die sie verarbeiten sollen, sobald sie verfügbar sind.
Bei einem Filterbefehl ist es einfach:
Alle Befehle werden gleichzeitig ausgeführt und
cmd1
beginnen mit dem Munch von Daten,src
sobald diese verfügbar sind.Mit drei Filterbefehlen können wir jetzt immer noch dasselbe tun: Starten Sie sie gleichzeitig und verbinden Sie sie mit Pipes:
Was wir mit Named Pipes relativ einfach machen können :
(Über das
} 3<&0
ist zu umgehen , dass&
Weiterleitungenstdin
von/dev/null
, und wir verwenden<>
, um das Öffnen der Rohre zu vermeiden, um zu blockieren, bis das andere Ende (cat
) auch geöffnet hat)Oder um
zsh
Named Pipes zu vermeiden, etwas schmerzhafter mit Coproc:Nun stellt sich die Frage: Wenn alle Programme gestartet und verbunden sind, fließen dann die Daten?
Wir haben zwei Einschränkungen:
tee
Alle Ausgaben werden mit derselben Rate eingespeist, sodass Daten nur mit der Rate der langsamsten Ausgabeleitung gesendet werden können.cat
beginnt erst mit dem Lesen von der zweiten Pipe (Pipe 6 in der obigen Zeichnung), wenn alle Daten von der ersten Pipe (5) gelesen wurden.Das bedeutet, dass die Daten in Pipe 6 erst
cmd1
nach Abschluss fließen . Und wie imtr b B
obigen Fall kann dies bedeuten, dass Daten auch nicht in Pipe 3 fließen, was bedeutet, dass sie in keinem der Pipes 2, 3 oder 4 fließen, datee
Feeds mit der langsamsten Rate von allen 3 erfolgen.In der Praxis haben diese Pipes eine Größe ungleich Null, so dass einige Daten durchkommen, und auf meinem System kann ich zumindest erreichen, dass es funktioniert bis zu:
Darüber hinaus mit
Wir haben eine Sackgasse, in der wir uns in dieser Situation befinden:
Wir haben die Rohre 3 und 6 gefüllt (jeweils 64 kB).
tee
hat gelesen , dass zusätzliches Byte, um es zu gefüttert hatcmd1
, abercmd2
, es zu leerencmd2
kann es nicht leeren, da es blockiert ist und darauf wartetcat
, es zu leerencat
kann es nicht leeren, da es wartet, bis keine Eingabe mehr in Pipe 5 erfolgt.cmd1
Ich kann nicht sagen, dasscat
es keine weiteren Eingaben mehr gibt, da es selbst auf weitere Eingaben von wartettee
.tee
kann nicht sagen, dasscmd1
es keine Eingabe mehr gibt, weil sie blockiert ist ... und so weiter.Wir haben eine Abhängigkeitsschleife und damit einen Deadlock.
Was ist nun die Lösung? Größere Pipes 3 und 4 (groß genug, um die gesamte
src
Ausgabe aufzunehmen) würden dies tun. Wir könnten das zum Beispiel tun, indem wirpv -qB 1G
zwischentee
undcmd2/3
wopv
bis zu 1 GB Daten einfügen, die darauf wartencmd2
undcmd3
sie lesen. Das würde jedoch zwei Dinge bedeuten:cmd2
die Datenverarbeitung in der Realität erst beginnen würde, wenn cmd1 fertig ist.Eine Lösung für das zweite Problem wäre, die Rohre 6 und 7 ebenfalls zu vergrößern. Wenn Sie dies voraussetzen
cmd2
undcmd3
so viel Leistung produzieren, wie sie verbrauchen, würde dies nicht mehr Speicher verbrauchen.Die einzige Möglichkeit, das Duplizieren der Daten zu vermeiden (im ersten Problem), besteht darin, die Aufbewahrung der Daten im Dispatcher selbst zu implementieren. Dies ist eine Variation davon
tee
, die Daten mit der Geschwindigkeit der schnellsten Ausgabe zuführen kann (Halten von Daten zum Zuführen der Daten) langsamer in ihrem eigenen Tempo). Nicht wirklich trivial.Das Beste, was wir vernünftigerweise ohne Programmierung erreichen können, ist wahrscheinlich so etwas wie (Zsh-Syntax):
quelle
+1
für die schöne ASCII-Kunst :-)Was Sie vorschlagen, kann nicht einfach mit einem vorhandenen Befehl ausgeführt werden und macht sowieso wenig Sinn. Die ganze Idee von Pipes (
|
unter Unix / Linux) ist, dass incmd1 | cmd2
diecmd1
Ausgabe (höchstens) geschrieben wird, bis ein Speicherpuffer voll ist, und danncmd2
Daten aus dem Puffer (höchstens) gelesen werden, bis er leer ist. Das heißt,cmd1
undcmd2
gleichzeitig ausgeführt, es ist nie erforderlich, dass mehr als eine begrenzte Datenmenge "im Flug" zwischen ihnen ist. Wenn Sie mehrere Eingänge mit einem Ausgang verbinden möchten, wenn einer der Leser hinter den anderen zurückbleibt, halten Sie entweder die anderen an (was ist der Grund dafür, parallel zu laufen?) Oder Sie verwahren den Ausgang, den der Nachzügler noch nicht gelesen hat (Was nützt es dann, keine Zwischendatei zu haben?) komplexer.In meinen fast 30 Jahren Erfahrung mit Unix kann ich mich an keine Situation erinnern, die für eine solche Pipe mit mehreren Ausgängen wirklich von Vorteil gewesen wäre.
Sie können heute mehrere Ausgaben in einem Stream kombinieren, nur nicht in irgendeiner Weise verschachtelt (wie sollten die Ausgaben von
cmd1
undcmd2
verschachtelt werden? Eine Zeile nach der anderen? Schreiben Sie abwechselnd 10 Bytes? Alternative "Absätze", die irgendwie definiert sind? Und wenn einer dies nicht tut?). lange nichts schreiben - all das ist kompliziert zu handhaben). Es ist geschehen, zB durch(cmd1; cmd2; cmd3) | cmd4
die Programmecmd1
,cmd2
undcmd3
einer nach dem anderen ausgeführt werden, wird die Ausgabe als Eingabe gesendetcmd4
.quelle
Für Ihr überlappendes Problem können Sie unter Linux (und mit
bash
oderzsh
aber nicht mitksh93
) Folgendes tun:Beachten Sie die Verwendung von
(...)
anstelle von{...}
, um bei jeder Iteration einen neuen Prozess zu erhalten, damit ein neues fd 3 auf ein neues verweistauxfile
.< /dev/fd/3
ist ein Trick, um auf diese jetzt gelöschte Datei zuzugreifen. Es wird nicht auf anderen Systemen als Linux funktionieren, auf denen< /dev/fd/3
es ähnlich ist,dup2(3, 0)
und daher würde fd 0 im Nur-Schreib-Modus mit dem Cursor am Ende der Datei geöffnet sein.Um die Verzweigung für die verschachtelte Funktion zu vermeiden, können Sie Folgendes schreiben:
Die Shell würde sich bei jeder Iteration darum kümmern , die fd 3 zu sichern. Die Dateideskriptoren würden Ihnen jedoch früher ausgehen.
Sie werden feststellen, dass dies effizienter ist als:
Verschachteln Sie also nicht die Umleitungen.
quelle