(bash) Script A, attendez le script B, mais pas son processus enfant

9

J'ai donc scriptA qui fait:

ssh server1 -- scriptB &
ssh server2 -- scriptB &
ssh server3 -- scriptB &
wait
otherstuffhappens

ScriptB fait:

rsync -av /important/stuff/. remoteserver:/remote/dir/.
rsync -av /not/so/important/stuff/. remoteserver:/remote/dir/. &
exit

Mon résultat souhaité est que scriptA attendra que toutes les instances de scriptB se terminent avant de continuer, ce qu'il fait actuellement, mais il attend également les rsyncs d'arrière-plan des choses pas si importantes. Ce sont des fichiers plus gros que je ne veux pas attendre.

J'ai lu Différence entre nohup, disown et & et j'ai essayé différentes combinaisons, mais je n'obtiens pas le résultat que je recherche.

À ce stade, je suis assez perplexe. Toute aide serait appréciée!

Apoc
la source

Réponses:

15

Le problème ici est sshdqu'attend la fin du fichier sur le canal, il lit la sortie standard de la commande (pas la sortie stderr pour une raison quelconque, au moins avec la version sur laquelle je teste). Et le travail d'arrière-plan hérite d'un fd pour ce canal.

Donc, pour contourner cela, redirigez la sortie de cette rsynccommande d' arrière-plan vers un fichier ou /dev/nullsi vous ne vous en souciez pas. Vous devez également rediriger stderr, car même si sshd n'attend pas le canal correspondant, après les sshdsorties, le canal sera cassé et rsyncsera donc tué s'il essaie d'écrire sur stderr.

Donc:

rsync ... > /dev/null 2>&1 &

Comparer:

$ time ssh localhost 'sleep 2 &'
ssh localhost 'sleep 2 &'  0.05s user 0.00s system 2% cpu 2.365 total
$ time ssh localhost 'sleep 2 > /dev/null &'
ssh localhost 'sleep 2 > /dev/null &'  0.04s user 0.00s system 12% cpu 0.349 total

Et:

$ ssh localhost '(sleep 1; ls /x; echo "$?" > out) > /dev/null &'; sleep 2; cat out
141  # ls by killed with SIGPIPE upon writing the error message
$ ssh localhost '(sleep 1; ls /x; echo "$?" > out) > /dev/null 2>&1 &'; sleep 2; cat out
2    # ls exited normally after writing the error on /dev/null instead
     # of a broken pipe
Stéphane Chazelas
la source
4

Écrivez un script qui est le parent des deux, puis vous pouvez facilement contrôler les deux . Vous pouvez également établir un canal de communication entre les deux canaux .

Pour ignorer spécifiquement certains travaux en arrière-plan, vous pouvez capturer le PID ( $!est le dernier PID du travail en arrière-plan) et wait $pidattendre uniquement que ce travail se termine.

l0b0
la source
3

Le -fdrapeau pour sshrésoudre le problème. Testé sur:

#!/bin/sh -e
echo $$_script__begin
( echo sleeping; sleep 2; echo slept )&
echo $$_script__end

Lorsque je l'exécute avec ssh localhost ./script, il attend jusqu'à ce que slepts'affiche. Avec l' -findicateur, il se ferme à echo $$_script__endet sleptapparaît plus tard en arrière-plan après le sshretour de la commande.

PSkocik
la source
2

Il s'agit d'un problème connu du serveur OpenSSH, qui est décrit et discuté dans le bugzilla amont # 2071 . Dans le bug, plusieurs solutions de contournement sont proposées soit du côté d'OpenSSH, mais aussi pour le script.

Si vous souhaitez attendre la sortie des scripts, vous devez également ajouter un waitavant exitdu scriptB.

Si vous ne vous souciez pas de la sortie, utilisez une variante de la nohupredirection IO vers /dev/null, ce qui résoudra le problème de la même manière.

Jakuje
la source
1

Vous pouvez essayer ça. $!est la variable shell par défaut qui contient l'ID de processus du pipeline / processus d'arrière-plan le plus récemment exécuté.

command1 &
lpid1=$!

command2 &
lpid2=$!

command3 &
lpid=$!

wait $lpid1  # waits for only the process with PID lpid1  to complete. 

Vous devez utiliser cela selon votre script en utilisant une exportvariable, etc.

Kaushik Nayak
la source
1

B a juste besoin d'attendre ses propres processus d'arrière-plan:

rsync -av /important/stuff/. remoteserver:/remote/dir/.
rsync -av /not/so/important/stuff/. remoteserver:/remote/dir/. &
wait
exit
Jared Lovell
la source
À ce stade, vous pouvez tout aussi bien ne pas exécuter le deuxième rsync en arrière-plan et éviter de l'utiliser waitentièrement. Bien que je devine ce que l'OP voulait faire, c'était d'exécuter les deux rsyncprocessus en parallèle, ce qui signifierait les mettre en arrière-plan tous les deux (avec &) puis les utiliser wait. Dans tous les cas, je conviens que c'est la façon la plus simple de résoudre le problème et que c'est celle que je choisirais en fonction des informations contenues dans la question.
David Z