Führen Sie mehrere parallele Befehle gleichzeitig im selben Terminal aus

78

Ich möchte einige Befehle ausführen, von denen jeder erst beendet wird, wenn Strg-C gedrückt wird. Gibt es etwas, das ich ausführen kann, um alle gleichzeitig auszuführen, und Strg-C beendet sie alle? Sie können den Terminalausgang gemeinsam nutzen.

Insbesondere habe ich den Kompass-Compiler, den Coffeescript-Compiler und einen benutzerdefinierten Befehl, der nach Dateiänderungen sucht, die alle ausgeführt werden und nach Dateiänderungen suchen. Ich möchte nicht für jeden Befehl ein Terminal laden.

Oliver Zheng
quelle

Antworten:

122

Dieses Bash-Skript ist für N parallele Threads. Jedes Argument ist ein Befehl.

trapbeendet alle Unterprozesse, wenn SIGINT abgefangen wird.
wait $PID_LISTwartet auf den Abschluss jedes Vorgangs. Wenn alle Prozesse abgeschlossen sind, wird das Programm beendet.

#!/bin/bash

for cmd in "$@"; do {
  echo "Process \"$cmd\" started";
  $cmd & pid=$!
  PID_LIST+=" $pid";
} done

trap "kill $PID_LIST" SIGINT

echo "Parallel processes have started";

wait $PID_LIST

echo
echo "All processes have completed";

Speichern Sie dieses Skript als parallel_commandsund machen Sie es ausführbar.
So verwenden Sie dieses Skript:

parallel_commands "cmd arg0 arg1 arg2" "other_cmd arg0 arg2 arg3"

Beispiel:

parallel_commands "sleep 1" "sleep 2" "sleep 3" "sleep 4"

Starten Sie 4 parallelen Schlaf und warten Sie, bis "Schlaf 4" beendet ist.

Alessandro Pezzato
quelle
3
Sehr cool. Falle ist das, wonach ich gesucht habe. Ich habe Ihre Antwort so bearbeitet wait, dass sie unnötige "Prozess x beendet" -Nachrichten unterdrückt.
Oliver Zheng
1
@ MTsoul, waitist besser als while-Schleife. Ich habe mein Skript mit Ihrem Vorschlag verbessert. Es funktioniert jetzt auch für N parallele Befehle.
Alessandro Pezzato
Was ist mit jedem cmd auf einem einzelnen Kern? Sollte ich wie folgt ausführen: ./parallel_commands "Taskset -c 0 cmd arg0 arg1 arg2" "Taskset -c 1 other_cmd arg0 arg2 arg3" oder Taskset -c 2 ./parallel_commands "Taskset -c 0 cmd arg0 arg1 arg2" "Taskset -c 1 other_cmd arg0 arg2 arg3 "
user2517676
So großartig. Jetzt habe ich einige Befehle, die in Bash Scrip sind und ich möchte sie parallel ausführen ?
shgnInc
1
Möchten Sie STDOUT oder den Exit-Wert?
Alessandro Pezzato
84

Basierend auf dem Kommentar von @ alessandro-pezzato. Führen Sie mehrere Befehle aus, indem Sie &zwischen den Befehlen verwenden.

Beispiel:

$ sleep 3 & sleep 5 & sleep 2 &

Die Befehle werden im Hintergrund ausgeführt.

nsantana
quelle
1
Das habe ich gesucht.
TWR Cole
1
Hallo @OleTange, ich kenne den Unterschied nicht. Ich habe es versucht &!und &beide haben gleich gearbeitet.
Nsantana
Danke für deinen Rat. Ich ändere den Code und entferne das Ausrufezeichen.
Nsantana
Control-C beendet nicht alle parallelen Befehle vollständig, wenn sie so ausgeführt werden. Sie werden weiterhin im Hintergrund ausgeführt. Was ist der richtige Weg, um die Ausführung aller Befehle zu beenden?
Cal S
Führen Sie fg aus und drücken Sie dann Strg + C, wiederholen Sie den
Vorgang
35

Verwenden Sie GNU Parallel:

(echo command1; echo command2) | parallel
parallel ::: command1 command2

Töten:

parallel ::: command1 command2 &
PID=$!
kill -TERM $PID
kill -TERM $PID
Ole Tange
quelle
1
@Oatman: Es sollte sein, aber GNU parallel ist nicht in allen Distributionen. Zum Beispiel scheint RHEL 7 kein Paket zur Verfügung zu haben.
Stefan Majewsky
Ist es möglich, die PID des parallelen Prozesses zu verfolgen, der mehrere Prozesse startet, und sie alle durch Beenden des übergeordneten parallelen Prozesses zu beenden?
WM
@StefanMajewsky: parallelbefindet sich im EPEL-Repository.
Onnonymous
Die Zitierwarnung / Lärm ist wirklich nervig! Es verschmutzt die Ausgabe, Sie können es ja zum Schweigen bringen, aber warum zum Teufel sollte man das tun? Stellen Sie sich das Chaos vor, wenn alle Open Source-Tools dies getan hätten!
Javad Sadeqzadeh
@JavadSadeqzadeh Haben Sie git.savannah.gnu.org/cgit/parallel.git/tree/doc/…
Ole Tange
3

Dies kann mit einem einfachen Makefile erfolgen:

sleep%:
        sleep $(subst sleep,,$@)
        @echo $@ done.

Verwenden Sie -jOption.

$ make -j sleep3 sleep2 sleep1
sleep 3
sleep 2
sleep 1
sleep1 done.
sleep2 done.
sleep3 done.

Ohne -jOption wird es seriell ausgeführt.

$ make -j sleep3 sleep2 sleep1
sleep 3
sleep3 done.
sleep 2
sleep2 done.
sleep 1
sleep1 done.

Sie können auch mit der Option -n trocken laufen.

$ make -j -n sleep3 sleep2 sleep1
sleep 3
sleep 2
sleep 1
Hiroshi
quelle
1

Ich schlage ein viel einfacheres Dienstprogramm vor, das ich gerade geschrieben habe. Es heißt derzeit par, wird aber bald in parl oder pll umbenannt, hat sich noch nicht entschieden.

https://github.com/k-bx/par

API ist so einfach wie:

par "script1.sh" "script2.sh" "script3.sh"

Präfixbefehle können erfolgen über:

par "PARPREFIX=[script1] script1.sh" "script2.sh" "script3.sh"
Konstantine Rybnikov
quelle
1
Nachdem ich versucht hatte und nicht das getan habe, was Sie oben mit gnu parallel beschrieben haben, war ich beim ersten Versuch mit Ihrem Dienstprogramm erfolgreich. Gute Arbeit!
Worldsayshi
Der erste: parallel ::: "script1.sh" "script2.sh" "script3.sh"Der zweite würde normalerweise --tag: parallel --tag ::: "script1.sh" "script2.sh" "script3.sh"
Ole Tange
-12

Um mehrere Befehle auszuführen, fügen Sie einfach &&zwei Befehle wie folgt hinzu:command1 && command2

Und wenn Sie sie in zwei verschiedenen Terminals ausführen möchten, gehen Sie folgendermaßen vor:

gnome-terminal -e "command1" && gnome-terminal -e "command2"

Dadurch werden 2 Terminals mit diesen geöffnet command1und command2ausgeführt.

Hoffe das hilft dir.

Lord Hypersonic
quelle
4
Das ist falsch. Es läuft command2 nach command1 und nur wenn es command1gelingt. Versuchen sleep 3 && sleep 3Sie es und Sie werden sehen, dass es 6s dauert.
Mark Setchell
Befehl1 && Befehl2 wird nicht sofort ausgeführt. Es wird nacheinander ausgeführt (unter Berücksichtigung des Erfolgsszenarios für den Befehl).
Gahan