Ich versuche, einige Befehle parallel und im Hintergrund mit Bash auszuführen. Folgendes versuche ich zu tun:
forloop {
//this part is actually written in Perl
//call command sequence
print `touch .file1.lock; cp bigfile1 /destination; rm .file1.lock;`;
}
Der Teil zwischen den Backticks (``) erzeugt eine neue Shell und führt die Befehle nacheinander aus. Die Sache ist, die Kontrolle über das ursprüngliche Programm kehrt erst zurück, nachdem der letzte Befehl ausgeführt wurde. Ich möchte die gesamte Anweisung im Hintergrund ausführen (ich erwarte keine Ausgabe-/Rückgabewerte) und ich möchte, dass die Schleife weiterläuft.
Das aufrufende Programm (das mit der Schleife) würde erst beendet, wenn alle erzeugten Muscheln beendet sind.
Ich könnte Threads in Perl verwenden, um verschiedene Threads zu erzeugen, die andere Shells nennen, aber es scheint ein Overkill ...
Kann ich eine Shell starten, Befehle geben und sagen, dass sie in den Hintergrund gehen soll?
Ich habe das nicht getestet, aber wie
print `(touch .file1.lock; cp bigfile1 /destination; rm .file1.lock;) &`;
Die Klammern bedeuten Ausführen in einer Subshell, aber das sollte nicht schaden.
Danke Hugh, das hat es geschafft:
[email protected]:~$ (echo "started"; sleep 15; echo "stopped")
started
stopped
[email protected]:~$ (echo "started"; sleep 15; echo "stopped") &
started
[1] 7101
[email protected]:~$ stopped
[1]+ Done ( echo "started"; sleep 15; echo "stopped" )
[email protected]:~$
Die anderen Ideen funktionieren nicht, weil sie jeden Befehl im Hintergrund starten und nicht die Befehlssequenz (was in meinem Fall wichtig ist!).
Danke nochmal!
Eine andere Möglichkeit ist die folgende Syntax:
{ command1; command2; command3; } &
wait
Beachten Sie, dass der &
am Ende der Befehlsgruppe steht und nicht nach jedem Befehl. Das Semikolon nach dem letzten Befehl ist ebenso erforderlich wie das Leerzeichen nach der ersten und vor der letzten Klammer. Die Variable wait
am Ende stellt sicher, dass der übergeordnete Prozess nicht beendet wird, bevor der erzeugte untergeordnete Prozess (die Befehlsgruppe) endet.
Sie können auch ausgefallene Dinge tun, wie stderr
und stdout
umleiten:
{ command1; command2; command3; } 2>&2 1>&1 &
Ihr Beispiel würde so aussehen:
forloop() {
{ touch .file1.lock; cp bigfile1 /destination; rm .file1.lock; } &
}
# ... do some other concurrent stuff
wait # wait for childs to end
for command in $commands
do
"$command" &
done
wait
Das kaufmännische Und-Zeichen am Ende des Befehls führt es im Hintergrund aus, und die Variable wait
wartet, bis die Hintergrundaufgabe abgeschlossen ist.
GavinCattell hat die nächste (für bash, IMO), aber wie Mad_Ady darauf hinweist, würde es die "lock" -Dateien nicht behandeln. Das sollte:
Wenn noch andere Jobs anstehen, wartet auch wait darauf. Wenn Sie auf nur die Kopien warten müssen, können Sie diese PIDs sammeln und nur auf diese warten. Wenn nicht, könnten Sie die 3 Zeilen mit "pids" löschen, aber es ist allgemeiner.
Außerdem habe ich eine Überprüfung hinzugefügt, um die Kopie insgesamt zu vermeiden:
pids=
for file in bigfile*
do
# Skip if file is not newer...
targ=/destination/$(basename "${file}")
[ "$targ" -nt "$file" ] && continue
# Use a lock file: ".fileN.lock" for each "bigfileN"
lock=".${file##*/big}.lock"
( touch $lock; cp "$file" "$targ"; rm $lock ) &
pids="$pids $!"
done
wait $pids
Im Übrigen sieht es so aus, als würden Sie neue Dateien in ein FTP-Repository (oder ähnliches) kopieren. Wenn ja, betrachten Sie könnte anstelle der Sperrdateien eine Strategie zum Kopieren/Umbenennen (dies ist jedoch ein anderes Thema).
Die Einrichtung in bash, nach der Sie suchen, heißt Compound Commands
. Weitere Informationen finden Sie in der Manpage:
Zusammengesetzte Befehle Ein zusammengesetzter Befehl ist einer der folgenden:
(list) list is executed in a subshell environment (see COMMAND EXECUTION ENVIRONMENT below). Variable assignments and builtin commands that affect the Shell's environment do not remain in effect after the command completes. The return status is the exit status of list. { list; } list is simply executed in the current Shell environment. list must be terminated with a newline or semicolon. This is known as a group command. The return status is the exit status of list. Note that unlike the metacharac‐ ters ( and ), { and } are reserved words and must occur where a reserved Word is permitted to be recognized. Since they do not cause a Word break, they must be separated from list by whitespace or another Shell metacharac‐ ter.
Es gibt andere, aber dies sind wahrscheinlich die 2 häufigsten Typen. Die erste, die Parens, führt eine Reihe von Befehlen in Reihe in einer Subshell aus, während die zweite, die geschweiften Klammern, eine Liste von Befehlen in Reihe in der aktuellen Shell anzeigt.
% ( date; sleep 5; date; )
Sat Jan 26 06:52:46 EST 2013
Sat Jan 26 06:52:51 EST 2013
% { date; sleep 5; date; }
Sat Jan 26 06:52:13 EST 2013
Sat Jan 26 06:52:18 EST 2013
Ich bin über diesen Thread hier gestolpert und habe mich dazu entschlossen, ein Code-Snippet zusammenzustellen, um verkettete Anweisungen als Hintergrundjobs zu erzeugen. Ich habe dies auf BASH für Linux, KSH für IBM AIX und ASH für Android von Busybox getestet. Ich denke, es ist sicher, dass es auf any Bourne-ähnlicher Shell funktioniert.
processes=0;
for X in `seq 0 10`; do
let processes+=1;
{ { echo Job $processes; sleep 3; echo End of job $processes; } & };
if [[ $processes -eq 5 ]]; then
wait;
processes=0;
fi;
done;
Dieser Code führt eine Reihe von Hintergrundjobs bis zu einer bestimmten Anzahl gleichzeitiger Jobs aus. Sie können dies zum Beispiel verwenden, um eine Vielzahl von gezippten Dateien mit xz
erneut zu komprimieren, ohne dass eine große Anzahl von xz
-Prozessen Ihren gesamten Speicher beansprucht und Ihren Computer zum Wachsen bringt: In diesem Fall verwenden Sie *
als Liste und den Stapel von for
Job wäre gzip -cd "$X" | xz -9c > "${X%.gz}.xz"
.
Führen Sie den Befehl mit einem at-Job aus:
# date
# jue sep 13 12:43:21 CEST 2012
# at 12:45
warning: commands will be executed using /bin/sh
at> command1
at> command2
at> ...
at> CTRL-d
at> <EOT>
job 20 at Thu Sep 13 12:45:00 2012
Das Ergebnis wird per E-Mail an Ihr Konto gesendet.
führen Sie die Befehle in einer Subshell aus:
(command1 ; command2 ; command3) &
Versuchen Sie, Befehle in geschweiften Klammern mit & s zu setzen:
{command1 & ; command2 & ; command3 & ; }
Dadurch wird keine Sub-Shell erstellt, sondern die Befehlsgruppe wird im Hintergrund ausgeführt.
HTH
Ich weiß nicht, warum niemand mit der richtigen Lösung geantwortet hat:
my @children;
for (...) {
...
my $child = fork;
exec "touch .file1.lock; cp bigfile1 /destination; rm .file1.lock;" if $child == 0;
Push @children, $child;
}
# and if you want to wait for them to finish,
waitpid($_) for @children;
Dies bewirkt, dass Perl untergeordnete Befehle ausführt, und Sie können warten, bis alle untergeordneten Elemente abgeschlossen sind, bevor Sie fortfahren.
Apropos,
print `some command`
und
system "some command"
gibt den gleichen Inhalt an stdout aus, der erste hat jedoch einen höheren Overhead, da Perl alle Ausgaben von "some command
" erfassen muss
Gabelung in einer for-Schleife:
for i in x; do ((a; b; c;)&); done
Beispiel:
for i in 500 300 100; do ((printf "Start $i: "; date; dd if=/dev/zero of=testfile_$i bs=1m count=$i 2>/dev/null; printf "End $i: "; date;)&) && sleep 1; done
Nur für den Fall, dass jemand interessiert ist, können Sie dies tun, ohne eine Subshell wie folgt aufzurufen:
print `touch .file1.lock && cp bigfile1 /destination && rm .file1.lock &`;
Sie können den Befehl GNU parallel
verwenden, um Jobs parallel auszuführen. Es ist sicherer sind schneller.
Meine Vermutung ist, dass Sie versuchen, mehrere große Dateien von der Quelle zum Ziel zu kopieren. Und dazu können Sie dies parallel mit der folgenden Anweisung tun.
$ ls *|parallel -kj0 --eta 'cp {} /tmp/destination'
Da wir die Option -j0
verwendet haben, werden alle Dateien parallel kopiert. Wenn Sie die Anzahl der parallelen Prozesse reduzieren müssen, können Sie -j<n>
verwenden, wobei <n>
die Anzahl der auszuführenden parallelen Prozesse ist.
Parallel sammelt auch die Ausgabe des Prozesses und meldet ihn sequentiell (mit der Option -k
), was andere Jobsteuerungsmechanismen nicht können.
Die --eta
-Option liefert eine detaillierte Statistik des laufenden Prozesses. So können wir wissen, wie der Prozess abgeschlossen wurde und wie lange es dauern wird, bis der Vorgang abgeschlossen ist.
Sie können Parameter an eine Befehlsgruppe übergeben (mit sequentiellen Befehlen) und diese im Hintergrund ausführen.
for hrNum in {00..11};
do
oneHour=$((10#$hrNum + 0))
secondHour=$((10#$hrNum + 12))
{ echo "$oneHour"; echo "$secondHour"; } &
wait
done