Domanda

Sto cercando di eseguire alcuni comandi in parallelo, in background, usando bash. Ecco cosa sto cercando di fare:

forloop {
  //this part is actually written in perl
  //call command sequence
  print `touch .file1.lock; cp bigfile1 /destination; rm .file1.lock;`;
}

La parte tra i backtick (``) genera una nuova shell ed esegue i comandi in successione. Il fatto è che il controllo al programma originale ritorna solo dopo che è stato eseguito l'ultimo comando. Vorrei eseguire l'intera istruzione in background (non mi aspetto alcun valore output / return) e vorrei che il ciclo continuasse a funzionare.

Il programma chiamante (quello che ha il ciclo) non sarebbe terminato fino al termine di tutte le shell generate.

Potrei usare i thread in perl per generare thread diversi che chiamano shell diverse, ma sembra un eccesso ...

Posso avviare una shell, dargli un set di comandi e dirgli di andare in background?

È stato utile?

Soluzione

Non ho provato questo, ma che dire

print `(touch .file1.lock; cp bigfile1 /destination; rm .file1.lock;) &`;

Le parentesi indicano l'esecuzione in una subshell ma ciò non dovrebbe far male.

Altri suggerimenti

Grazie Hugh, è stato così:

adrianp@frost:~$ (echo "started"; sleep 15; echo "stopped")
started
stopped
adrianp@frost:~$ (echo "started"; sleep 15; echo "stopped") &
started
[1] 7101
adrianp@frost:~$ stopped

[1]+  Done                    ( echo "started"; sleep 15; echo "stopped" )
adrianp@frost:~$ 

Le altre idee non funzionano perché avviano ogni comando in background e non la sequenza di comandi (che è importante nel mio caso!).

Grazie ancora!

Un altro modo è usare la sintassi seguente:

{ command1; command2; command3; } &
wait

Nota che & va alla fine del gruppo di comandi, non dopo ogni comando. Il punto e virgola dopo il comando finale è necessario, così come lo spazio dopo la prima parentesi e prima della parentesi finale. wait alla fine garantisce che il processo padre non venga interrotto prima della fine del processo figlio generato (il gruppo di comando).

Puoi anche fare cose fantasiose come il reindirizzamento stderr e stdout:

{ command1; command2; command3; } 2>&2 1>&1 &

Il tuo esempio sarebbe simile:

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

La e commerciale alla fine del comando lo esegue in background e wait attende fino al completamento dell'attività in background.

GavinCattell ha ottenuto il più vicino (per bash , IMO), ma come sottolineato da Mad_Ady, non gestirà il & Quot; lock & Quot; File. Questo dovrebbe:

Se ci sono altri lavori in sospeso, anche aspetta li attenderà. Se devi attendere solo le copie, puoi accumulare quei PID e attendere solo quelli. In caso contrario, è possibile eliminare le 3 righe con & Quot; pids & Quot; ma è più generale.

Inoltre, ho aggiunto il controllo per evitare del tutto la copia:

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

Per inciso, sembra che tu stia copiando nuovi file in un repository FTP (o simile). In tal caso, potresti prendere in considerazione una strategia di copia / rinomina anziché i file di blocco (ma questo è un altro argomento).

La funzione in bash che stai cercando si chiama Compound Commands. Vedi la pagina man per maggiori informazioni:

  

Comandi composti          Un comando composto è uno dei seguenti:

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

Ce ne sono altri, ma questi sono probabilmente i 2 tipi più comuni. Il primo, le parentesi, eseguirà un elenco di comandi in serie in una subshell, mentre il secondo, le parentesi graffe, sarà un elenco di comandi in serie nella shell corrente.

parentesi

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

parentesi graffe

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

Esegui il comando utilizzando un at at:

# 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

Il risultato verrà inviato al tuo account via mail.

Mi sono imbattuto in questo thread qui e ho deciso di mettere insieme uno snippet di codice per generare istruzioni concatenate come processi in background. Ho provato questo su BASH per Linux, KSH per IBM AIX e ASH di Busybox per Android, quindi penso che sia sicuro dire che funziona su qualsiasi shell tipo Bourne.

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;

Questo codice esegue un numero di lavori in background fino a un certo limite di lavori simultanei. Puoi usarlo, ad esempio, per ricomprimere molti file compressi con xz senza che un enorme gruppo di * processi consumi tutta la tua memoria e faccia vomitare il tuo computer: in questo caso, usi for come l'elenco di gzip -cd "$X" | xz -9c > "${X%.gz}.xz" e il processo batch sarebbero <=>.

esegui i comandi in una subshell:

(command1 ; command2 ; command3) &

Prova a mettere i comandi tra parentesi graffe con & amp; s, in questo modo:

{command1 & ; command2 & ; command3 & ; }

Questo non crea una sotto-shell, ma esegue il gruppo di comandi in background.

HTH

Non so perché nessuno abbia risposto con la soluzione corretta:

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;

Questo fa sì che Perl spawn i bambini esegua ciascun comando e ti consente di attendere il completamento di tutti i bambini prima di procedere.

A proposito,

print `some command`

e

system "some command"

genera lo stesso contenuto su stdout, ma il primo ha un overhead più alto, poiché Perl deve catturare tutto l'output di " some command "

Forking in a for loop:

for i in x; do ((a; b; c;)&); done

Esempio:

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

Nel caso in cui qualcuno sia ancora interessato, puoi farlo senza chiamare una subshell come questa:

print `touch .file1.lock && cp bigfile1 /destination && rm .file1.lock &`;

È possibile utilizzare il comando GNU parallel per eseguire lavori in parallelo. È più sicuro sono più veloci.

La mia ipotesi è che stai cercando di copiare più file di grandi dimensioni dall'origine alla destinazione. E per questo puoi farlo in parallelo con la seguente istruzione.

$ ls *|parallel -kj0 --eta 'cp {} /tmp/destination'

Dato che abbiamo usato l'opzione -j0, tutti i file verranno copiati in parallelo. Nel caso in cui sia necessario ridurre il numero di processi paralleli, è possibile utilizzare -j<n> dove <n> è il numero di processi paralleli da eseguire.

Parallel raccoglierà anche l'output del processo e lo riporterà in modo sequenziale (con l'opzione -k) che altri meccanismi di controllo del lavoro non possono eseguire.

L'opzione

--eta ti fornirà una statistica dei dettagli del processo in corso. Quindi possiamo sapere come potrebbe essere stato completato il processo e quanto tempo ci vorrà per terminare.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top