it-swarm.com.de

bash: Startet mehrere verkettete Befehle im Hintergrund

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?

43
Mad_Ady

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.

29
Hugh Allen

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!

19
Mad_Ady

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
18
lechup
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.

14
GavinCattell

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).

5
NVRAM

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.

parens

% ( date; sleep 5; date; )
Sat Jan 26 06:52:46 EST 2013
Sat Jan 26 06:52:51 EST 2013

geschweifte Klammern

% { date; sleep 5; date; }
Sat Jan 26 06:52:13 EST 2013
Sat Jan 26 06:52:18 EST 2013
3
slm

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".

2
RAKK

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.

2
Eliseo Carrasco

führen Sie die Befehle in einer Subshell aus:

(command1 ; command2 ; command3) &
2
Radu Simionescu

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

1
Zsolt Botykai

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

1
ephemient

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

1
Nikolas Britton

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.

0
Kannan Mohan

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
0
user7668