suspendre un script bash jusqu'à la fin des commandes précédentes

20

J'ai un script bash qui ressemble à ceci:

##script
#!/bin/bash
rm data*
rm logfile*
for i in {1..30}
do
## append a & if you want to run it parallel;
nohup Rscript --vanilla main.R 10 100 $i &> logfile"$i" &
done

Je voudrais créer une autre boucle for après la première pour continuer pendant 30. Par exemple

##script
#!/bin/bash
rm data*
rm logfile*
for i in {1..30}
do
## append a & if you want to run it parallel;
nohup Rscript --vanilla main.R 10 100 $i &> logfile"$i" &

for i in {31..60}
do
## append a & if you want to run it parallel;
nohup Rscript --vanilla main.R 10 100 $i &> logfile"$i" &
done

Je voudrais que le premier ensemble de travaux se termine avant de commencer le nouvel ensemble. Mais à cause de nohupcela, il semble qu'ils soient tous exécutés simultanément.

Je l'ai nohupparce que je me connecte à distance à mon serveur et que je démarre les travaux là-bas, puis ferme mon bash. Existe-t-il une solution alternative?

masfenix
la source
1
Recherchez le manuel pour le waitbuiltin.
Satō Katsura

Réponses:

22

Vous voudrez utiliser la waitcommande pour le faire pour vous. Vous pouvez soit capturer tous les ID de processus enfants et les attendre spécifiquement, ou s'ils sont les seuls processus d'arrière-plan que votre script crée, vous pouvez simplement appeler waitsans argument. Par exemple:

#!/bin/bash
# run two processes in the background and wait for them to finish

nohup sleep 3 &
nohup sleep 10 &

echo "This will wait until both are done"
date
wait
date
echo "Done"
ParanoidGeek
la source
6

Quelques points:

  • Si votre objectif nohupest d'empêcher une sortie de shell distante de tuer vos processus de travail, vous devez l'utiliser nohupsur le script lui-même, et non sur les processus de travail individuels qu'il crée.

  • Comme expliqué ici , nohupempêche uniquement les processus de recevoir SIGHUP et d'interagir avec le terminal, mais il ne rompt pas la relation entre le shell et ses processus enfants.

  • En raison du point ci-dessus, avec ou sans nohup, un simple waitentre les deux forboucles provoquera l' forexécution de la seconde uniquement après la fin de tous les processus enfants démarrés par la première for.

  • Avec un simple wait:

    tous les processus enfants actuellement actifs sont attendus et le statut de retour est zéro.

  • Si vous devez exécuter le second foruniquement s'il n'y a pas eu d'erreurs dans le premier, vous devrez enregistrer chaque PID de travailleur avec $!et les transmettre à wait:

    pids=
    for ...
        worker ... &
        pids+=" $!"
    done
    wait $pids || { echo "there were errors" >&2; exit 1; }
Matei David
la source
Il peut y avoir d'autres travaux en cours d'exécution sur le serveur. Je voudrais donc seulement attendre mon lot .. ce sont des scripts R donc ils sont exécutés sous Rou cc1plusdans la topcommande
masfenix
Je voudrais également utiliser nohup à l'intérieur pour exécuter toutes les commandes en "parallèle". ce sont essentiellement des simulations pour un programme scientifique. Je veux exécuter 180 simulations au total, mais par lots de 60. Le compteur doit également passer de 1 à 180. Si je les fais une à la fois, cela prendra trop de temps.
masfenix
waitfait bashattendre les travaux d'arrière-plan qu'il a engendré lui-même, rien d'autre. Il peut y avoir une certaine confusion ici - ces forboucles, les avez-vous enregistrées dans un fichier et les avez-vous appelées en tant que script (ce que je supposais, à cause de la ##scriptligne), ou les saisissez-vous à la main dans le terminal?
Matei David
-1

Utilisez la fonction fgintégrée. Il attend la fin des processus d'arrière-plan.

Essayez help fgpour plus de détails.

Luchostein
la source
Un script s'exécute sans contrôle des travaux.
Kusalananda
-1

Si vous insérez quelque chose comme le segment de code suivant entre vos deux forboucles, cela pourrait aider.

flag=0

while [ flag -eq 0 ]
do
  ps -ef | grep "Rscript --vanilla" | grep -v grep > /dev/null
  flag=${?}
  sleep 10
done

Bien sûr, si votre application Rscripta une chance de ne pas aboutir et de s'attarder, votre deuxième boucle for peut ne pas avoir de chance de s'exécuter. Le segment de code ci-dessus suppose que tous les processus avec l'identifiant Rscript --vanillase termineront et disparaîtront correctement. Sans savoir ce que fait votre application et comment elle fonctionne, je dois me fier à cette hypothèse.

ÉDITER

À la lumière des commentaires, cela conviendrait mieux à vos besoins. (il comprend votre code d'origine ainsi que la logique de vérification de l'achèvement)

for i in {1..30}
do
## append a & if you want to run it parallel;
nohup Rscript --vanilla main.R 10 100 $i &> logfile"$i" &
pids[$i]=${!}
done

flag=0

while [ flag -eq 0 ] 
do
  for PID in $(echo ${pids[@]})
  do
    flag=1
    ps -ef | grep ${PID} | grep -v grep >/dev/null; r=${?}
    if [ ${r} -eq 0 ]
    then 
      flag=0
    fi
  done
done

for i in {31..60}
do
## append a & if you want to run it parallel;
nohup Rscript --vanilla main.R 10 100 $i &> logfile"$i" &
done
MelBurslan
la source
Le nom du processus dans tops'affiche Rparfois ou cc1plus.
masfenix
Dans ce cas, vous devrez trouver un dénominateur commun, apparaissant dans la ps -efliste. Ou après chaque nohupcommande, enregistrez le PID dans une variable (de préférence un tableau) par echo ${!}et vérifiez ce groupe de PID. Quand ils disparaissent tous, vous pouvez passer à la deuxième forboucle
MelBurslan