Bash-Skript-Verarbeitung begrenzte Anzahl von Befehlen parallel

196

Ich habe ein Bash-Skript, das so aussieht:

#!/bin/bash
wget LINK1 >/dev/null 2>&1
wget LINK2 >/dev/null 2>&1
wget LINK3 >/dev/null 2>&1
wget LINK4 >/dev/null 2>&1
# ..
# ..
wget LINK4000 >/dev/null 2>&1

Aber jede Zeile zu verarbeiten, bis der Befehl beendet ist, und dann zur nächsten zu wechseln, ist sehr zeitaufwändig. Ich möchte zum Beispiel 20 Zeilen gleichzeitig verarbeiten. Wenn sie fertig sind, werden weitere 20 Zeilen verarbeitet.

Ich dachte wget LINK1 >/dev/null 2>&1 &daran, den Befehl in den Hintergrund zu senden und weiterzumachen, aber hier gibt es 4000 Zeilen. Dies bedeutet, dass ich Leistungsprobleme haben werde, ganz zu schweigen davon, dass die Anzahl der Prozesse, die ich gleichzeitig starten sollte, begrenzt ist. Dies ist also nicht gut Idee.

Eine Lösung, an die ich gerade denke, besteht darin, zu überprüfen, ob einer der Befehle noch ausgeführt wird oder nicht. Zum Beispiel kann ich nach 20 Zeilen diese Schleife hinzufügen:

while [  $(ps -ef | grep KEYWORD | grep -v grep | wc -l) -gt 0 ]; do
sleep 1
done

Natürlich muss ich in diesem Fall & an das Ende der Zeile anhängen! Aber ich bin der Meinung, dass dies nicht der richtige Weg ist.

Wie gruppiere ich also tatsächlich alle 20 Zeilen und warte, bis sie fertig sind, bevor ich zu den nächsten 20 Zeilen übergehe. Dieses Skript wird dynamisch generiert, damit ich während der Generierung alles tun kann, was ich will, aber es muss NICHT benutze wget, es war nur ein Beispiel, also wird mir jede Lösung, die wget-spezifisch ist, nichts nützen.

AL-Kateb
quelle
1
waitist hier die richtige Antwort, aber Sie while [ $(ps …wären viel besser geschrieben while pkill -0 $KEYWORD…- mit proctools ... das heißt, aus legitimen Gründen, um zu überprüfen, ob ein Prozess mit einem bestimmten Namen noch läuft.
Kojiro
Ich denke, diese Frage sollte wieder geöffnet werden. Bei der "möglichen doppelten" Qualitätssicherung geht es darum, eine begrenzte Anzahl von Programmen parallel auszuführen. Wie 2-3 Befehle. Diese Frage konzentriert sich jedoch auf das Ausführen von Befehlen in z. B. einer Schleife. (siehe "aber es gibt 4000 Zeilen").
VasiliNovikov
@VasyaNovikov Haben Sie alle Antworten auf diese Frage und das Duplikat gelesen ? Jede einzelne Antwort auf diese Frage finden Sie auch in den Antworten auf die doppelte Frage. Das ist genau die Definition einer doppelten Frage. Es macht absolut keinen Unterschied, ob Sie die Befehle in einer Schleife ausführen oder nicht.
RobinCTS
@robinCTS Es gibt Schnittpunkte, aber die Fragen selbst sind unterschiedlich. Außerdem beziehen sich 6 der beliebtesten Antworten auf die verknüpfte Qualitätssicherung nur auf 2 Prozesse.
VasiliNovikov
2
Ich empfehle, diese Frage erneut zu öffnen, da ihre Antwort klarer, sauberer, besser und viel besser bewertet ist als die Antwort auf die verknüpfte Frage, obwohl sie drei Jahre jünger ist.
Dan Nissenbaum

Antworten:

331

Verwenden Sie das waiteingebaute:

process1 &
process2 &
process3 &
process4 &
wait
process5 &
process6 &
process7 &
process8 &
wait

Für das obige Beispiel würden 4 Prozesse process1... process4im Hintergrund gestartet, und die Shell würde warten, bis diese abgeschlossen sind, bevor sie mit dem nächsten Satz beginnen.

Aus dem GNU-Handbuch :

wait [jobspec or pid ...]

Warten Sie, bis der untergeordnete Prozess, der von jeder Job-ID für die Prozess-ID oder Jobspezifikation angegeben wird, beendet wird, und geben Sie den Beendigungsstatus des zuletzt warteten Befehls zurück. Wenn eine Jobspezifikation angegeben ist, wird auf alle Prozesse im Job gewartet. Wenn keine Argumente angegeben werden, wird auf alle derzeit aktiven untergeordneten Prozesse gewartet, und der Rückgabestatus ist Null. Wenn weder jobspec noch pid einen aktiven untergeordneten Prozess der Shell angeben, lautet der Rückgabestatus 127.

devnull
quelle
14
Also im Grundei=0; waitevery=4; for link in "${links[@]}"; do wget "$link" & (( i++%waitevery==0 )) && wait; done >/dev/null 2>&1
Kojiro
18
Dies ist eine schlechte Idee, es sei denn, Sie sind sicher, dass jeder Prozess genau zur gleichen Zeit abgeschlossen wird. Sie müssen neue Jobs starten, um die aktuelle Gesamtzahl der Jobs auf einer bestimmten Obergrenze zu halten. Parallel ist die Antwort.
rsaw
1
Gibt es eine Möglichkeit, dies in einer Schleife zu tun?
DomainsFeatured
Ich habe dies versucht, aber es scheint, dass Variablenzuweisungen, die in einem Block vorgenommen wurden, im nächsten Block nicht verfügbar sind. Liegt das daran, dass es sich um separate Prozesse handelt? Gibt es eine Möglichkeit, die Variablen an den Hauptprozess zurückzugeben?
Bobby
97

Siehe parallel . Die Syntax ähnelt der xargs, aber die Befehle werden parallel ausgeführt.

Choroba
quelle
13
Dies ist besser als die Verwendung wait, da neue Jobs abgeschlossen werden müssen, während alte abgeschlossen sind, anstatt darauf zu warten, dass ein ganzer Stapel abgeschlossen ist, bevor mit dem nächsten begonnen wird.
Chepner
5
Wenn Sie beispielsweise die Liste der Links in einer Datei haben, können Sie dies tun, cat list_of_links.txt | parallel -j 4 wget {}wodurch jeweils vier wgetSekunden ausgeführt werden.
Herr Lama
5
Es gibt ein neues Kind in der Stadt namens pexec, das ein Ersatz für ist parallel.
Slashsbin
2
Ein Beispiel zu
liefern
1
parallel --jobs 4 < list_of_commands.shDabei ist list_of_commands.sh eine Datei mit einem einzelnen Befehl (z. B. wget LINK1Notiz ohne &) in jeder Zeile. Möglicherweise muss CTRL+Zund bgdanach, um es im Hintergrund laufen zu lassen.
weiji14
71

In der Tat xargs können Befehle parallel für Sie ausgeführt werden. Dafür gibt es eine spezielle -P max_procsBefehlszeilenoption. Siehe man xargs.

Vader B.
quelle
2
+100 das ist großartig, da es eingebaut und sehr einfach zu bedienen ist und in einem Einzeiler gemacht werden kann
Clay
Ideal für kleine Container, da keine zusätzlichen Pakete / Abhängigkeiten benötigt werden!
Marco Roy
1
Siehe diese Frage für Beispiele: stackoverflow.com/questions/28357997/…
Marco Roy
7

Sie können 20 Prozesse ausführen und den folgenden Befehl verwenden:

wait

Ihr Skript wartet und fährt fort, wenn alle Hintergrundjobs abgeschlossen sind.

Binpix
quelle