bash: începe mai multe comenzi înlănțuiți în fundal

voturi
37

Am încercat să fac niște comenzi în paralel, în fundal, folosind bash. Iată ce am încercat să fac:

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

Partea dintre backticks ( ``) dă naștere la un nou shell și execută comenzile în succesiune. Lucru este, de control la programul inițial returnează numai după ultima comandă a fost executată. Aș dori să execute întreaga declarație în fundal (Nu mă așteptam valori de ieșire / retur) și aș dori bucla de a continua să ruleze.

Programul de asteptare (cel care are bucla) nu se va termina până când toate cojile zămislite termina.

Aș putea folosi fire în perl pentru a da naștere la fire diferite, care numesc scoici diferite, dar se pare că o ... nejustificată

Pot începe o coajă, da un set de comenzi și spune că pentru a merge în fundal?

Întrebat 02/10/2008 la 06:50
sursa de către utilizator
În alte limbi...                            


15 răspunsuri

voturi
13
for command in $commands
do
    "$command" &
done
wait

Ampersand la sfârșitul comenzii se execută în fundal, și waitașteaptă până când sarcina de fundal este finalizată.

Publicat 02/10/2008 la 07:02
sursa de către utilizator

voturi
1

Încercați să puneți comenzi în acolade cu & S, cum ar fi acest lucru:

{command1 & ; command2 & ; command3 & ; }

Acest lucru nu creează un sub-shell, dar executa grupul de comenzi în fundal.

HTH

Publicat 02/10/2008 la 07:05
sursa de către utilizator

voturi
27

Nu am testat acest lucru, dar cum despre

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

Parantezele înseamnă executa într-un subshell, dar care nu ar trebui sa doara.

Publicat 02/10/2008 la 07:24
sursa de către utilizator

voturi
16

Mulțumesc Hugh, care a făcut-o:

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:~$ 

Celelalte idei nu funcționează pentru că încep fiecare comandă în fundal, și nu secvența de comandă (ceea ce este important în cazul meu!).

Iti multumesc din nou!

Publicat 02/10/2008 la 07:42
sursa de către utilizator

voturi
1

Nu știu de ce nimeni nu a răspuns cu soluția corectă:

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;

Acest lucru face ca Perl pentru a da naștere la copii pentru a rula fiecare comandă, și vă permite să așteptați pentru toți copiii pentru a finaliza înainte de a continua.

Apropo,

print `some command`

și

system "some command"

ieșire același conținut la stdout, dar primul are o mai mare deasupra capului, ca Perl are pentru a captura toate " some commandieșire„'s

Publicat 03/10/2008 la 20:45
sursa de către utilizator

voturi
5

GavinCattell a primit cel mai aproape (pentru bash, OMI), dar , după cum a subliniat Mad_Ady, nu s - ar ocupa de „blocare“ fișiere. Asta ar trebui:

Dacă există alte locuri de muncă în așteptare, De așteptare va aștepta pentru cei care , de asemenea. Dacă trebuie să așteptați numai copiile, puteți acumula aceste PIDs și așteptați doar cei. Dacă nu, ai putea șterge cele 3 linii cu „PIDs“ , dar este mai general.

În plus, am adăugat verificare pentru a evita copia cu totul:

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

De altfel, se pare că copiați fișiere noi la un depozit FTP (sau similar). Dacă da, ați putea lua în considerare o copie / redenumi strategie în loc de fișiere de blocare (dar asta e un alt subiect).

Publicat 05/11/2009 la 19:11
sursa de către utilizator

voturi
2

Rulați comanda folosind un loc de muncă la:

# 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

Rezultatul va fi trimis la contul dvs. prin e-mail.

Publicat 13/09/2012 la 09:52
sursa de către utilizator

voturi
3

Facilitatea în bash pe care o căutați este numit Compound Commands. A se vedea pagina de om pentru mai multe informatii:

Compusul Comenzi O comandă compus este una dintre următoarele:

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

Sunt altele, dar acestea sunt, probabil, cele 2 tipuri de cele mai comune. Primul, parens, va rula o listă de comandă în serie într-un subshell, iar al doilea, acolade, va o listă de comenzi în serie în shell curent.

Parens

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

acolade

% { date; sleep 5; date; }
Sat Jan 26 06:52:13 EST 2013
Sat Jan 26 06:52:18 EST 2013
Publicat 26/01/2013 la 10:05
sursa de către utilizator

voturi
14

O altă modalitate este de a folosi sintaxa:

{ command1; command2; command3; } &
wait

Nu există nici o comandă , dar si dupa unul este la sfârșitul grupului cmd. waitLa capăt , care a dat nastere asigurată copilul va avea mamă până se termină.

Puteți face , de asemenea , chestii de lux cum ar fi redirecționarea stderrși stdout:

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

Exemplul tău ar arăta:

forloop() {
    { touch .file1.lock; cp bigfile1 /destination; rm .file1.lock; } &
}
# ... do some other concurrent stuff
wait # wait for childs to end
Publicat 26/09/2013 la 12:15
sursa de către utilizator

voturi
2

Am dat peste acest thread aici și a decis să pună împreună un fragment de cod pentru a da naștere la declarații ca locuri de muncă de fundal înlănțuit. Am testat acest lucru pe BASH pentru Linux, KSH pentru IBM AIX si ASH Busybox pentru Android, deci cred că e sigur să spun că funcționează pe orice shell Bourne asemănătoare.

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;

Acest cod se execută un număr de locuri de muncă de fond până la o anumită limită de locuri de muncă concurente. Puteți utiliza acest lucru, de exemplu, pentru a recompress o mulțime de fișiere gzip cu xzfără a avea o grămadă mare de xzprocese mânca întreaga memorie și de a face computerul arunca în sus: în acest caz, utilizați *ca fore listă și locuri de muncă lot ar fie gzip -cd "$X" | xz -9c > "${X%.gz}.xz".

Publicat 08/04/2014 la 20:48
sursa de către utilizator

voturi
0

Doar în cazul în care cineva este în continuare interesat, puteți face fără a apela la un subshell ca aceasta:

print `touch .file1.lock && cp bigfile1 /destination && rm .file1.lock &`;
Publicat 27/10/2014 la 11:44
sursa de către utilizator

voturi
1

Bifurcare într-o buclă:

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

Exemplu:

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

Publicat 01/11/2014 la 00:53
sursa de către utilizator

voturi
0

Aveți posibilitatea să utilizați GNU parallelcomandă pentru a rula de locuri de muncă în paralel. Este mai sigur sunt mai rapide.

Parerea mea este că încercați să copiați mai multe fișiere de mari dimensiuni de la sursă la destinație. Și pentru că se poate face acest lucru în paralel cu declarația de mai jos.

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

Așa cum am folosit -j0opțiune, toate fișierele vor fi copiate în paralel. În cazul în care, dacă aveți nevoie pentru a reduce numărul de proces paralel , atunci puteți folosi în -j<n>cazul în care <n>este numărul de proces paralel care urmează să fie executat.

Paralel va colecta , de asemenea , ieșirea procesului și să raporteze într - un mod secvențial (cu -kopțiune) care alt mecanism de control de locuri de muncă nu se poate face.

--etaopțiune vă va da detalii o statistica a procesului care se întâmplă. Deci, putem ști cum poate procesului au fost finalizate și cât timp va lua pentru a obține terminat.

Publicat 01/11/2014 la 05:23
sursa de către utilizator

voturi
1

rula comenzile intr-un subshell:

(command1 ; command2 ; command3) &
Publicat 19/11/2016 la 08:18
sursa de către utilizator

voturi
0

Puteți trece parametri la un grup de comandă (având comenzi secvențiale) și să le ruleze în fundal.

for hrNum in {00..11};
do
    oneHour=$((10#$hrNum + 0))
    secondHour=$((10#$hrNum + 12))
    { echo "$oneHour"; echo "$secondHour"; } &
    wait
done
Publicat 10/07/2019 la 07:31
sursa de către utilizator

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more