Question

Contexte:

J'ai un script bash qui contient un sous-shell et un piège pour le pseudosignal EXIT, et il ne capte pas correctement les interruptions lors d'un rsync.Voici un exemple :

#!/bin/bash
logfile=/path/to/file;
directory1=/path/to/dir
directory2=/path/to/dir

cleanup () {
     echo "Cleaning up!"
     #do stuff
     trap - EXIT 
}

trap '{
    (cleanup;) | 2>&1 tee -a $logfile
}' EXIT

(
    #main script logic, including the following lines:
    (exec sleep 10;);        
    (exec rsync --progress -av --delete $directory1 /var/tmp/$directory2;);

)  | 2>&1 tee -a $logfile
trap - EXIT #just in case cleanup isn't called for some reason

L'idée du script est la suivante :la plupart de la logique importante s'exécute dans un sous-shell qui passe par tee et dans un fichier journal, donc je n'ai pas à le faire tee chaque ligne de la logique principale pour que tout soit enregistré.Chaque fois que le sous-shell se termine ou que le script est arrêté pour une raison quelconque (le pseudosignal EXIT devrait capturer tous ces cas), le piège l'interceptera et exécutera le cleanup() fonction, puis retirez le piège.Le rsync et sleep les commandes (le sleep n'est qu'un exemple) sont exécutées exec pour empêcher la création de processus zombies si je tue le script parent pendant son exécution, et que chaque commande potentiellement longue est enveloppée dans son propre sous-shell de sorte que lorsque exec se termine, cela ne mettra pas fin à tout le script.

Le problème:

Si j'interromps le script (via kill ou CTRL+C) pendant l'exécution/sous-shell encapsulé sleep commande, le piège fonctionne correctement, et je vois « Nettoyage ! » résonner et enregistré.Si j'interromps le script pendant le rsync commande, je vois rsync terminer et écrire rsync error: received SIGINT, SIGTERM, or SIGHUP (code 20) at rsync.c(544) [sender=3.0.6] à l'écran, puis le script meurt ;pas de nettoyage, pas de piégeage.Pourquoi une interruption/un meurtre de rsync déclencher le piège ?

J'ai essayé d'utiliser le --no-detach switcher avec rsync, mais cela n'a rien changé.J'ai bash 4.1.2, rsync 3.0.6, centOS 6.2.

Était-ce utile?

La solution

Que diriez-vous de simplement avoir toutes les sorties de Point X être redirigé vers vous sans avoir à le répéter partout et en désordre avec toutes les sous-coquilles et exécutions ... (espérons que je n'ai pas manqué quelque chose)

#!/bin/bash
logfile=/path/to/file;
directory1=/path/to/dir
directory2=/path/to/dir

exec > >(exec tee -a $logfile) 2>&1

cleanup () {
     echo "Cleaning up!"
     #do stuff
     trap - EXIT 
}
trap cleanup EXIT

sleep 10
rsync --progress -av --delete $directory1 /var/tmp/$directory2

Autres conseils

En plus de set -e, je pense que tu veux set -E:

S'il est défini, toute interruption sur ERR est héritée par les fonctions shell, les substitutions de commandes et les commandes exécutées dans un environnement sous-shell.Le piège ERR n'est normalement pas hérité dans de tels cas.

Alternativement, au lieu d'encapsuler vos commandes dans des sous-shell, utilisez des accolades qui vous donneront toujours la possibilité de rediriger les sorties des commandes mais les exécuteront dans le shell actuel.

L'interruption sera correctement interceptée si vous ajoutez INT au piège

trap '{
    (cleanup;) | 2>&1 tee -a $logfile
}' EXIT INT

Bash capture correctement les interruptions.Cependant, cela ne répond pas à la question de savoir pourquoi le script se bloque à la sortie si sleep est interrompu, ni pourquoi il ne se déclenche pas rsync, mais fait fonctionner le script comme il est censé le faire.J'espère que cela t'aides.

Votre shell peut être configuré pour se fermer en cas d'erreur :

bash # enter subshell
set -e
trap "echo woah" EXIT
sleep 4

Si vous interrompez sleep (^C), le sous-shell se fermera à cause de set -e et imprimer woah Dans le processus.

Aussi, légèrement sans rapport :ton trap - EXIT est dans un sous-shell (explicitement), donc cela n'aura aucun effet après le retour de la fonction de nettoyage

Il ressort assez clairement de l'expérimentation que rsync se comporte comme d'autres outils tels que ping et n'hérite pas des signaux du parent Bash appelant.

Vous devez donc faire preuve d'un peu de créativité et faire quelque chose comme ce qui suit :

$ cat rsync.bash
#!/bin/sh

 set -m
 trap '' SIGINT SIGTERM EXIT
 rsync -avz LargeTestFile.500M root@host.mydom.com:/tmp/. &
 wait

 echo FIN

Maintenant, quand je le lance :

$ ./rsync.bash
X11 forwarding request failed
building file list ... done
LargeTestFile.500M
^C^C^C^C^C^C^C^C^C^C
sent 509984 bytes  received 42 bytes  92732.00 bytes/sec
total size is 524288000  speedup is 1027.96
FIN

Et nous pouvons voir que le fichier a été entièrement transféré :

$ ll -h | grep Large
-rw-------. 1  501 games 500M Jul  9 21:44 LargeTestFile.500M

Comment ça fonctionne

L'astuce ici est que nous disons à Bash via set -m pour désactiver les contrôles des tâches sur toutes les tâches en arrière-plan qu'il contient.Nous étudions ensuite le contexte rsync puis exécuter un wait commande qui attendra la dernière commande exécutée, rsync, jusqu'à ce qu'il soit terminé.

Nous gardons ensuite l'intégralité du script avec le trap '' SIGINT SIGTERM EXIT.

Les références

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