bash: lancer plusieurs commandes enchaînées en arrière-plan

j'essaie de lancer des commandes dans paralel, 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;`;
}
"151910920 ` la partie entre backticks (" ) génère un nouveau shell et exécute les commandes successivement. Le fait est que le contrôle du programme original ne retourne que lorsque la dernière commande a été exécutée. Je voudrais exécuter l'instruction entière en arrière-plan (Je ne m'attends pas à des valeurs de sortie/retour) et je voudrais que la boucle continue exécuter.

le programme appelant (celui qui a la boucle) ne s'arrêterait pas tant que tous les shells générés ne seraient pas terminés.

je pourrais utiliser des threads en perl pour générer des threads différents qui appellent des coquillages différents, mais cela semble exagéré...

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

41
demandé sur nwinkler 2008-10-02 11:50:25

14 réponses

je n'ai pas testé mais si 151920920"

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

les parenthèses signifient exécuter dans une cellule mais cela ne devrait pas faire de mal.

29
répondu Hugh Allen 2008-10-02 08:24:17

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 parce qu'elles démarrent chaque commande en arrière-plan, et pas la séquence de commande (ce qui est important dans mon cas!).

Merci encore!

17
répondu Mad_Ady 2008-10-02 08:42:23

une Autre façon est d'utiliser la syntaxe:

{ command1; command2; command3; } &
wait

il n'y a pas de commande & après mais une à la fin du groupe cmd. Le wait à la fin garantit que l'enfant engendré aura un parent jusqu'à ce qu'il se termine.

vous pouvez également faire des choses de fantaisie comme 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
16
répondu lechup 2013-09-26 13:15:45
for command in $commands
do
    "$command" &
done
wait

L'esperluette à la fin de la commande s'exécute en arrière-plan, et le wait attend jusqu'à ce que la tâche d'arrière-plan est terminé.

13
répondu GavinCattell 2015-08-25 18:16:07

GavinCattell a obtenu le plus proche (pour bash, IMO), mais comme Mad_Ady a souligné, il ne serait pas gérer les fichiers "lock". Cela devrait:

S'il y a d'autres jobs en attente, le wait attendra ceux-là aussi. Si vous devez attendre seulement les copies, Vous pouvez accumuler ces PID et attendre seulement ceux. Si pas, vous pouvez supprimer les 3 lignes avec "pid", mais c'est plus général.

en plus, j'ai ajouté la vérification pour éviter la copie tout à fait:

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 semble que vous copiez de nouveaux fichiers vers un dépôt FTP (ou similaire). Si c'est le cas, vous pourrait envisager une stratégie de copie/renommer au lieu des fichiers de verrouillage (mais c'est un autre sujet).

5
répondu NVRAM 2017-05-23 12:10:10

L'installation de bash que vous cherchez s'appelle Compound Commands . Voir la page de manuel pour plus d'informations:

Commandes Composées Une commande composée est l'une des suivantes:

   (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 communs. La première, parens, se déroulera une liste de commande en série dans un shell interne est exécuté, tandis que le second, les accolades, une liste de commandes en série dans la coquille actuelle.

parens

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

broches bouclées

% { date; sleep 5; date; }
Sat Jan 26 06:52:13 EST 2013
Sat Jan 26 06:52:18 EST 2013
3
répondu slm 2013-01-26 12:05:49

exécuter 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.

2
répondu Eliseo Carrasco 2012-09-13 10:52:40

je suis tombé sur ce fil ici et a décidé de mettre en place un code snippet pour engendrer des déclarations enchaînées comme des travaux de fond. J'ai testé cela sur BASH pour Linux, KSH pour IBM AIX et Ash de Busybox pour Android, donc je pense qu'il est sûr de dire que cela fonctionne sur n'importe quel shell Bourne-like.

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 d'emplois d'arrière-plan jusqu'à une certaine limite d'emplois concurrents. Vous pouvez l'utiliser, par exemple, pour recompresser beaucoup de gzippé les fichiers avec xz sans avoir un énorme tas de processus xz dévorent toute votre mémoire et font vomir votre ordinateur: dans ce cas, vous utilisez * comme la liste for et le travail par lots serait gzip -cd "$X" | xz -9c > "${X%.gz}.xz" .

2
répondu RAKK 2016-08-14 22:25:27

essayez de mettre des commandes dans des accolades bouclées avec &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

1
répondu Zsolt Botykai 2008-10-02 08:05:24

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

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 provoque Perl à engendrer des enfants pour exécuter chaque commande, et vous permet d'attendre que tous les enfants à compléter avant de procéder.

soit dit en passant,

print `some command`

et

system "some command"

sortie le même contenu à stdout, mais le premier a un plafond plus élevé, Perl doit capturer tout de " some command " 's sortie

1
répondu ephemient 2008-10-03 21:45:55

fourche dans une boucle pour:

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

1
répondu Nikolas Britton 2014-11-01 08:02:31

exécuter les commandes dans une sous-station:

(command1 ; command2 ; command3) &
1
répondu Radu Simionescu 2016-11-19 18:28:49

juste au cas où quelqu'un est toujours intéressé, vous pouvez le faire sans appeler un subshell comme ceci:

print `touch .file1.lock && cp bigfile1 /destination && rm .file1.lock &`;
0
répondu Anastasios Andronidis 2014-10-27 13:44:43

vous pouvez utiliser la commande GNU parallel pour exécuter des tâches en parallèle. Il est plus sûr sont plus rapides.

à mon avis, vous essayez de copier plusieurs gros fichiers 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. Dans le cas où vous avez besoin de réduire le nombre de processus parallèles, alors vous pouvez utiliser -j<n><n> est le nombre de processus parallèles à exécuter.

Parallel recueillera également le résultat du processus et le signalera de manière séquentielle (avec l'option -k ), ce que les autres mécanismes de contrôle des tâches ne peuvent pas faire.

--eta option vous donnera des statistiques détaillées du processus en cours. Nous pouvons donc savoir comment le processus a été achevé en mai et combien de temps cela prendra pour l'être.

0
répondu Kannan Mohan 2014-11-01 07:34:37