2012-03-08 16 views
9

Contesto:Bash non intrappolare gli interrupt durante rsync/subshell dichiarazioni exec

Ho uno script bash che contiene una subshell e una trappola per il pseudosignal EXIT, e non è intrappolando correttamente gli interrupt durante una rsync. Ecco un esempio:

#!/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'idea dello script è questo: la maggior parte della logica importante viene eseguito in una subshell che viene convogliata attraverso tee e ad un file di log, quindi non c'è bisogno di tee ogni singola riga di la logica principale per ottenere tutto registrato. Ogni volta che la subshell termina o lo script viene interrotto per qualsiasi motivo (lo pseudosignal EXIT deve acquisire tutti questi casi), il trap lo intercetterà ed eseguirà la funzione cleanup() e quindi rimuoverà il trap. I comandi rsync e sleep (il sonno è solo un esempio) vengono eseguiti tramite exec per impedire la creazione di processi zombie se si elimina lo script padre mentre sono in esecuzione e ogni comando potenzialmente a lunga esecuzione è racchiuso nella propria sottoshell in modo che al termine di exec, non verrà terminato l'intero script.

Il problema:

Se interrompo lo script (via kill o CTRL + C) durante l'exec/subshell avvolto sleep di comando, la trappola funziona correttamente, e vedo "Pulizia up!" echeggiato e registrato. Se interrompo lo script durante il comando rsync, vedo rsync e scrivo rsync error: received SIGINT, SIGTERM, or SIGHUP (code 20) at rsync.c(544) [sender=3.0.6] sullo schermo, quindi lo script muore; nessuna pulizia, nessuna trappola. Perché l'interruzione/uccisione di rsync non attiva la trappola?

Ho provato a utilizzare lo switch --no-detach con rsync, ma non ha modificato nulla. Ho bash 4.1.2, rsync 3.0.6, centOS 6.2.

+0

Questo non è il motivo del tuo problema ma la tua registrazione non è affidabile, perché scrivi sullo stesso file con due programmi diversi contemporaneamente. – ceving

+0

il tuo 'trap - EXIT' si trova in una subshell (esplicitamente), quindi non avrà un effetto dopo che la funzione cleanup restituisce – sehe

+0

Esecuzione in esecuzione in una subshell equivale a eseguire il comando normalmente - non è necessario il punteggiatura extra. –

risposta

1

ne dite solo avere tutta l'uscita dal punto X reindirizzato al tee senza dover ripetere ovunque e la mensa con tutti i sub-conchiglie e dirigenti ... (speranza non ho perso qualcosa)

#!/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 
+0

Questa era in realtà la mia prima soluzione, ma alcuni dei nostri ambienti non possono effettuare il reindirizzamento a causa di una versione strana di Bash o qualcosa del genere .... quindi ho fatto ricorso al "avvolgere tutto in un blocco e puntare tutto su tee "hack". Buon punto però. –

+0

@ZacB Puoi essere più specifico sulla "versione strana di bash"? In alcune situazioni (quando eseguito come '/ bin/sh') esegue n modalità di compatibilità Posix ... Se questo è il caso aggiungere' set + o posix' prima di 'exec' – nhed

+0

Questa era la situazione @nhed; è stato risolto passando a '/ bin/bash'. –

1

L'interrupt verrà correttamente catturato se si aggiunge INT alla trappola

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

Bash è intrappolando correttamente gli interrupt. Tuttavia, questa non è la domanda, perché lo script intercetta in uscita se sleep viene interrotto, né perché non si attiva su rsync, ma fa in modo che lo script funzioni come previsto. Spero che questo ti aiuti.

+0

Sembrava giusto, ma l'ho implementato e ha avuto l'opposto dell'effetto desiderato. Se aggiungo INT alle chiamate trap, l'istruzione 'sleep' non attiva più il trap, e anche 'rsync' non lo ha ancora. Quando rimuovo INT (non cambiando nient'altro) 'sleep' innesca nuovamente la trappola. –

+0

Rimuovi l'ultima trap "just in case" e lo script dovrebbe funzionare bene anche con i trap EXIT. INT nella trappola che chiama cleanup dovrebbe essere il modo corretto per gestire gli interrupt, comunque. Non correlato, ma come se ha scritto, la trappola nella sottotitola non fa nulla. –

0

La shell può essere configurato per uscire in caso di errore:

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

Se si interrompe sleep (^ C), allora il subshell uscirà a causa di set -e e stampare woah nel processo.

Inoltre, un po 'estraneo: la tua trap - EXIT è in una subshell (esplicitamente), in modo da non avere un effetto dopo la funzione di pulizia ritorna

1

Oltre a set -e, Penso che si desidera set -E:

If set, any trap on ERR is inherited by shell functions, command substitutions, and commands executed in a sub‐shell environment. The ERR trap is normally not inherited in such cases.

In alternativa, invece di racchiudere i comandi nelle sottochiavi utilizzate le parentesi graffe che vi daranno comunque la possibilità di reindirizzare gli output di comando ma li eseguiranno nella shell corrente.