kombiniere die Ausgabe von zwei Befehlen in bash

81

Ist es möglich, die Ausgabe dieser beiden Befehle zu kombinieren?

node ~/projects/trunk/index.js 
python ~/projects/trunk/run.py run

Keiner der Befehle wird beendet, daher bin ich mir nicht sicher, wie ich das machen soll.

chovy
quelle
3
Wenn die Programme nicht beendet werden, schreiben sie vermutlich ununterbrochen eine Ausgabe? Was willst du mit ihrer Ausgabe machen? Zeilen verschachteln, ...? Warum willst du das machen?
Vonbrand
2
Der Knotenbefehl gibt nicht viel aus, muss jedoch ausgeführt werden. Der Python One gibt alle Anfragen aus, ich möchte beide erfassen und sie beide im selben Shell-Fenster ansehen.
Chovy

Antworten:

108

Sie können zwei Befehle kombinieren, indem Sie sie gruppieren mit { }:

{ command1 & command2; }

so weit, können Sie die Gruppe in eine Datei umleiten können (letzte ;vor }zwingend erforderlich):

{ command1 & command2; } > new_file

Wenn Sie STDOUTund STDERRin zwei Dateien trennen möchten :

{ command1 & command2; } > STDOUT_file 2> STDERR_file
Gilles Quenot
quelle
3
Es spielt keine Rolle, dass die Programme nicht beendet werden. 'tail -f' "beendet" auch nicht, aber das funktioniert immer noch und kombiniert die Ausgaben beider Programme. Funktioniert auch für mehr als zwei Befehle. ^ c zum Beenden beendet nur einen der gruppierten Befehle. Sie müssen die anderen jedoch manuell töten.
SuperMagic
5
Scheint , wie Sie den letzten Mangel ;vor }, es ist zwingend erforderlich!
Gilles Quenot
2
Seien Sie gewarnt: Dies bewahrt nicht ganze Zeilen! Sie erhalten unzuverlässige Ausgaben, da die Zeilen teilweise aufgeteilt und untereinander verwechselt werden. Sie können dies versuchen, bei { yes {1..20} & yes {1..20}; } | grep -v '^1 2 3'dem im Idealfall nichts gedruckt wird, wenn die Zeilen nicht unterbrochen sind.
Antak
8
Ich würde lieber verwenden, &&anstatt &! command1 & command2- Dadurch wird command1 im Hintergrund ausgeführt und command2 sofort gestartet, wodurch beide Befehle parallel ausgeführt werden und die Ausgabe durcheinander gebracht wird. command1 && command2- Dadurch wird Befehl1 (im Vordergrund) ausgeführt. Wenn Befehl1 erfolgreich war, wird Befehl2 ausgeführt.
DUzun
1
@ DUZUN OP sagte, dass keiner der Befehle beendet wird. Mit Ihrer Lösung wird der zweite Befehl nie ausgeführt
Zoey Hewll,
50

Allgemeiner ist es möglich, entweder eine Subshell oder eine Befehlsgruppierung zu verwenden und die Ausgabe der gesamten Gruppe auf einmal umzuleiten.

Code:

( command1 ; command2 ; command3 ) | cat

{ command1 ; command2 ; command3 ; } > outfile.txt

Der Hauptunterschied zwischen den beiden besteht darin, dass der erste Teil eines untergeordneten Prozesses ausgeführt wird, während der zweite Teil im Kontext der Haupt-Shell ausgeführt wird. Dies kann Konsequenzen für die Einstellung und Verwendung von Variablen und anderen Umgebungseinstellungen sowie für die Leistung haben.

Vergessen Sie nicht, dass die schließende Klammer in der Befehlsgruppierung (und den Funktionen) durch ein Semikolon oder eine neue Zeile vom Inhalt getrennt sein muss. Dies liegt daran, dass "}"es sich tatsächlich um einen eigenen Befehl (ein eigenes Schlüsselwort) handelt, der wie ein solcher behandelt werden muss.

j9s
quelle
2
Die Weiterleitung von ( )funktioniert auch.
Muru
2
}ist überhaupt kein Befehl. Es ist ein reserviertes Wort. Gleiches gilt für {. Ich schreibe in der Regel solche Listen wie folgt: { command1;command2;} > outfile.txt. Sie können nach den Semikolons Leerzeichen einfügen, dies ist jedoch nicht erforderlich. Der Raum danach { ist jedoch notwendig.
Wildcard
1
Seien Sie gewarnt: Dies bewahrt nicht ganze Zeilen! Sie erhalten unzuverlässige Ausgaben, da die Zeilen teilweise aufgeteilt und untereinander verwechselt werden. Sie können dies versuchen, bei ( yes {1..20} & yes {1..20}; ) | grep -v '^1 2 3'dem im Idealfall nichts gedruckt wird, wenn die Zeilen nicht unterbrochen sind. (H / t zu @antak).
Ole Tange
3
Manchmal möchten Sie command2 nur ausführen, wenn command1 erfolgreich war:( command1 && command2 && command3 ) | cat
DUzun
Ich bevorzuge die runden Klammern ()als die geschweiften Klammern {}, die als Hintergrundfortschritt ausgeführt werden, und dann muss man sich mit der Ausgabe davon befassen. Pfeife auch zu Katze `| cat` ist eine schönere Alternative als `> / dev / stdout`
DarkMukke
2

Am Ende tat ich dies, die anderen Vorschläge funktionierten nicht, da der 2. Befehl entweder getötet oder nie ausgeführt wurde.

alias app () {
    nohup python ~/projects/trunk/run.py run 1>/tmp/log 2>&1 &
    echo $! > /tmp/api.pid
    nohup node ~/projects/trunk/index.js 1>/tmp/log 2>&1 &
    echo $! > /tmp/client.pid
    tail -f /tmp/log
}
chovy
quelle
1
Hinweis: Dies kann zu E / A-Fehlern führen, wenn die beiden Prozesse versuchen, "gleichzeitig" in die Datei zu schreiben.
Djizeus
2
kann 2 verschiedene Protokolldateien angeben und tun, tail -f *.logobwohl ich dies nie als Problem mit 2 verschiedenen Prozessen gesehen habe, die in dieselbe Protokolldatei schreiben.
Chovy
@chovy: u könnte Ihr Problem als Frage schreiben hier ... es ist nützlich
Abdennour TOUMI
1
Seien Sie gewarnt: Dies bewahrt nicht ganze Zeilen! Sie erhalten unzuverlässige Ausgaben, da die Zeilen teilweise aufgeteilt und untereinander verwechselt werden. Sie können dies mit command1 = yes {1..20}command2 = versuchen yes {1..20}und die kombinierte Ausgabe leiten, durch | grep -v '^1 2 3'die im Idealfall nichts gedruckt wird, wenn die Zeilen nicht unterbrochen sind. (H / t zu @antak).
Ole Tange
Darüber hinaus ist Ihre Festplatte möglicherweise voll, wenn die Datenmenge groß ist.
Ole Tange
2

Versuche dies:

paste $(node ~/projects/trunk/index.js) $(python ~/projects/trunk/run.py run) > outputfile
frogstarr78
quelle
1
Was macht "Paste"?
Chovy
@chovy, siehe hier: techrepublic.com/article/… Ich bin mir nicht sicher, ob es in diesem Kontext funktioniert.
FixMaker
Ich halte Paste hier nicht für angebracht, da sie dazu gedacht ist, Spalten nebeneinander zu platzieren
Bernhard,
@Bernhard in der Tat. Aber es wurde nicht in der
Anfrage
@frogstarr78 Ich halte es für sehr unwahrscheinlich, dass dies das ist, was er will, aber du hast recht, es ist nicht spezifiziert.
Bernhard
1

Die meisten der bisherigen Lösungen behandeln das Teilleitungsproblem schlecht. Nehmen Sie für eine Sekunde an, dass die Programme:

cmd1() {
    perl -e 'while(1) { print "a"x3000_000,"\n"}'
}
export -f cmd1
cmd2() {
    perl -e 'while(1) { print "b"x3000_000,"\n"}'
}
export -f cmd2

Wenn Sie diese parallel ausführen, möchten Sie, dass die Ausgabe vollständige Zeilen von as gefolgt von vollständigen Zeilen von bs enthält. Was Sie nicht wollen, ist das Mischen von as und bs in derselben Zeile ( tr -s abersetzt das Wiederholen von as durch ein einzelnes a, damit Sie leichter sehen können, was passiert):

# This is bad - half lines are mixed
$ (cmd1 & cmd2 ) | tr -s ab
bababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababa
ababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababab

Wenn Sie stattdessen GNU Parallel verwenden, erhalten Sie nette, saubere, vollständige Linien mit as oder bs, die jedoch niemals gemischt werden:

$ parallel --line-buffer ::: cmd1 cmd2 | tr -s ab
a
a
b
b
b
b
a

Neuere Versionen von GNU Parallel vermeiden sogar das Auffüllen Ihrer Festplatte: Die oben genannten können für immer ausgeführt werden.

Ole Tange
quelle
0

Da Sie bereits verwenden node, möchten Sie es möglicherweise gleichzeitig versuchen

Führen Sie mehrere Befehle gleichzeitig aus. Mag npm run watch-js & npm run watch-lessaber besser.

Tamlyn
quelle
0

Für den speziellen Fall, dass mehrere BASH-Befehlsausgaben in einer Zeile kombiniert werden, finden Sie hier ein Rezept, mit dem jeder Befehl nacheinander ausgeführt wird, wobei alle Zeilenumbrüche zwischen den Ausgaben entfernt werden.

(echo 'ab' && echo 'cd' && echo 'ef') | tr -d '\n'
>>> abcdef

Als reales Beispiel wird im folgenden Code eine ASCII-Nachricht zwischen zwei festen Bytefolgen eingebettet (in diesem Fall ein Druckbefehl).

#   hex prefix           encode a message as hex    hex suffix    | strip newline | hex to binary | (then, for example, send the binary over a TCP connection)
(echo '1b40' && echo "Test print #1" | xxd -p && echo '1d564103') | tr -d '\n'    | xxd -r -p     | nc -N 192.168.192.168 9100

(Hinweis: Diese Methode funktioniert nur, wenn die Befehle beendet werden. Informationen zum Kombinieren von stdout von Befehlen, die nicht beendet werden, finden Sie in den anderen Antworten.)

Luke
quelle
(1) Bitte zeigen Sie die (erwartete) Ausgabe Ihres zweiten Befehls an. (2) Bitte zeigen Sie, wie das OP diese Technik zur Lösung seines Problems einsetzen würde.
Scott
1) Die Ausgabe des zweiten Befehls ist nicht ascii-binär, daher wäre es nicht sinnvoll, ihn anzuzeigen. 2) OP hat wahrscheinlich sein spezifisches Problem zwischen 2013 und jetzt gelöst. Diese Frage ist nun praktisch eine Referenz zum Kombinieren der Standardausgabe mehrerer Bash-Befehle. Ich glaube, dass eine Technik zum Kombinieren dieser Befehle in einer Zeile ein nützliches "Rezept" ist, das hier zu erwähnen ist (da ich hierher gekommen bin und es gesucht und nicht gefunden habe) es).
Luke