Question

J'essaie d'exécuter des commandes en parallèle, en arrière-plan, en utilisant bash. Voici ce que j'essaie de faire:

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

La partie entre les backticks (``) génère un nouveau shell et exécute les commandes successivement. Le problème, c’est que le contrôle du programme original ne revient qu’après l’exécution de la dernière commande. J'aimerais exécuter la totalité de l'instruction en arrière-plan (je n'attends aucune valeur de sortie / retour) et j'aimerais que la boucle continue de s'exécuter.

Le programme appelant (celui qui a la boucle) ne se terminera pas tant que tous les obus créés ne seront pas terminés.

Je pourrais utiliser des threads en perl pour générer différents threads qui appellent différents shells, mais cela semble excessif ...

Puis-je démarrer un shell, lui donner un ensemble de commandes et lui dire d'aller en arrière-plan?

Était-ce utile?

La solution

Je n'ai pas testé cela, mais qu'en est-il

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

Les parenthèses signifient l’exécution dans un sous-shell mais cela ne devrait pas faire mal.

Autres conseils

Merci Hugh, qui l’a fait:

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

Les autres idées ne fonctionnent pas car elles lancent chaque commande en arrière-plan, et non la séquence de commandes (ce qui est important dans mon cas!).

Merci encore!

Une autre méthode consiste à utiliser la syntaxe suivante:

{ command1; command2; command3; } &
wait

Notez que le & va à la fin du groupe de commandes, pas après chaque commande. Le point-virgule après la dernière commande est nécessaire, de même que l'espace après le premier crochet et avant le dernier crochet. Le wait à la fin garantit que le processus parent n'est pas tué avant la fin du processus enfant généré (le groupe de commandes).

Vous pouvez également faire des choses fantaisistes telles que la redirection stderr et stdout:

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

Votre exemple ressemblerait à:

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 perluète à la fin de la commande l'exécute en arrière-plan et wait attend que la tâche en arrière-plan soit terminée.

GavinCattell a reçu le plus proche (pour bash , IMO), mais comme Mad_Ady l’a souligné, il ne gérera pas le & "Verrou &"; des dossiers. Cela devrait:

S'il existe d'autres tâches en attente, wait les attend également. Si vous devez attendre seulement les copies, vous pouvez accumuler ces PID et n'attendre que ceux-ci. Sinon, vous pouvez supprimer les 3 lignes avec & Quot; pids & Quot; mais c'est plus général.

De plus, j'ai ajouté une vérification pour éviter la copie:

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

Incidemment, il semblerait que vous copiez de nouveaux fichiers dans un référentiel FTP (ou similaire). Si tel est le cas, vous pourriez envisager une stratégie de copie / renommage au lieu des fichiers de verrouillage (mais c’est un autre sujet).

Le service que vous recherchez dans bash s'appelle Compound Commands. Consultez la page de manuel pour plus d’informations:

  

Commandes composées          Une commande composée est l’un des suivants:

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

Il y en a d'autres, mais ce sont probablement les 2 types les plus courants. Le premier, les parens, lancera une liste de commandes en série dans un sous-shell, tandis que le second, les accolades, affichera une liste de commandes en série dans le shell actuel.

parens

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

accolades

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

Exécutez la commande en utilisant un at job:

# 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

Le résultat sera envoyé à votre compte par courrier.

Je suis tombé sur ce fil ici et j'ai décidé de créer un extrait de code pour générer des instructions chaînées en tant que tâches d'arrière-plan. J'ai testé cela sur BASH pour Linux, KSH pour IBM AIX et ASH pour Android de Busybox. Je pense donc qu'il est prudent de dire que cela fonctionne sur n'importe quel shell similaire à 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;

Ce code exécute un certain nombre de tâches en arrière-plan, dans la limite des tâches concurrentes. Vous pouvez l’utiliser, par exemple, pour recompresser un grand nombre de fichiers gzippés avec xz sans qu'un grand nombre de processus * ne consomment toute votre mémoire et ne fassent pas jaillir votre ordinateur. Dans ce cas, vous utilisez for comme la liste de gzip -cd "$X" | xz -9c > "${X%.gz}.xz" et le travail par lots seraient <=>.

exécuter les commandes dans un sous-shell:

(command1 ; command2 ; command3) &

Essayez de mettre les commandes entre accolades avec & amp; s, comme ceci:

{command1 & ; command2 & ; command3 & ; }

Ceci ne crée pas de sous-shell, mais exécute le groupe de commandes en arrière-plan.

HTH

Je ne sais pas pourquoi personne n'a répondu avec la solution appropriée:

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;

Cela amène Perl à générer des enfants pour exécuter chaque commande et vous permet d'attendre que tous les enfants soient terminés avant de continuer.

Au fait,

print `some command`

et

system "some command"

affiche le même contenu sur stdout, mais le premier supporte un temps système plus important, car Perl doit capturer tous les " some command & sorties de

Déchiffrer une boucle for:

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

Exemple:

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

Si quelqu'un est toujours intéressé, vous pouvez le faire sans appeler un sous-shell comme celui-ci:

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

Vous pouvez utiliser la commande GNU parallel pour exécuter des travaux en parallèle. C'est plus sûr, plus vite.

Je suppose que vous essayez de copier plusieurs fichiers volumineux de la source à la destination. Et pour cela, vous pouvez le faire en parallèle avec la déclaration ci-dessous.

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

Comme nous avons utilisé l'option -j0, tous les fichiers seront copiés en parallèle. Si vous devez réduire le nombre de processus parallèles, vous pouvez utiliser -j<n><n> est le nombre de processus parallèles à exécuter.

Parallèle collectera également les résultats du processus et les signalera de manière séquentielle (avec l'option -k), ce qu'un autre mécanisme de contrôle des travaux ne peut pas exécuter.

L’option

--eta vous donnera des statistiques détaillées sur le processus en cours. Nous pouvons donc savoir si le processus est terminé et combien de temps faudra-t-il pour le terminer.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top