Domanda

Contesto:

Ho uno script bash che contiene una subshell e una trappola per lo pseudosegnale EXIT e non intrappola correttamente gli interrupt durante un 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 della sceneggiatura è questa:la maggior parte della logica importante viene eseguita in una subshell che viene convogliata tee e in un file di registro, quindi non è necessario tee ogni singola riga della logica principale per registrarla tutta.Ogni volta che la subshell termina, o lo script viene interrotto per qualsiasi motivo (lo pseudosegnale EXIT dovrebbe catturare tutti questi casi), la trappola lo intercetterà ed eseguirà il comando cleanup() funzione, quindi rimuovere la trappola.IL rsync E sleep vengono eseguiti i comandi (lo sleep è solo un esempio). exec per impedire la creazione di processi zombie se uccido lo script principale mentre sono in esecuzione e ogni comando potenzialmente di lunga esecuzione è racchiuso nella propria subshell in modo che quando exec termina, non terminerà l'intero script.

Il problema:

Se interrompo lo script (via kill o CTRL+C) durante il wrapper di exec/subshell sleep Comando, la trap funziona correttamente e vedo "Pulizia!" fatto eco e registrato.Se interrompo la sceneggiatura durante il rsync comando, vedo rsync finisci e scrivi rsync error: received SIGINT, SIGTERM, or SIGHUP (code 20) at rsync.c(544) [sender=3.0.6] sullo schermo, e poi la sceneggiatura muore;nessuna pulizia, nessuna cattura.Perché un'interruzione/uccisione di rsync innescare la trappola?

Ho provato a usare il --no-detach cambia con rsync, ma non ha cambiato nulla.Ho bash 4.1.2, rsync 3.0.6, centOS 6.2.

È stato utile?

Soluzione

Che ne dici di avere tutta la produzione dal punto x essere reindirizzato a tee senza doverlo ripeterlo ovunque e pasticcio con tutti i sottoboschi e gli execs ... (spero di non perdere 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
.

Altri suggerimenti

Oltre a set -e, penso che tu voglia set -E:

.

Se impostato, qualsiasi trap on err è ereditata da funzioni di shell, sostituzioni di comando e comandi eseguiti in un ambiente sub-shell.La trappola ERR non è normalmente ereditata in questi casi.

In alternativa, invece di avvolgere i comandi in sott'obberimenti Utilizzare bretelle ricci che ti darà ancora la possibilità di reindirizzare le uscite dei comandi, ma li eseguiranno nel guscio corrente.

L'interruzione sarà correttamente catturato se si aggiunge int alla trappola

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

Bash è intrappolare correttamente gli interrupt.Tuttavia, questo non è anwer la domanda, perché lo script trappole in uscita se sleep è interrotto, né perché non si attiva su rsync, ma rende lo script funzionante come dovrebbe.Spero che questo aiuti.

La tua shell potrebbe essere configurata per uscire in caso di errore:

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

Se interrompi sleep (^C), la subshell uscirà a causa di set -e e stampare woah nel processo.

Inoltre, leggermente non correlato:tuo trap - EXIT è in una subshell (esplicitamente), quindi non avrà effetto dopo il ritorno della funzione di pulizia

È abbastanza chiaro dalla sperimentazione che rsync si comporta come altri strumenti come ping e non ereditano segnali dal genitore bash chiamante.

Quindi devi ottenere un po 'creativo con questo e fare qualcosa come il seguente:

$ cat rsync.bash
#!/bin/sh

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

 echo FIN
.

Ora quando lo eseguo:

$ ./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
.

E possiamo vedere il file ha fatto il trasferimento completamente:

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

Come funziona

Il trucco Qui stiamo dicendo a Bash tramite set -m a disabilitare i controlli di lavoro su qualsiasi lavoro di sfondo al suo interno. Allora studiando il rsync e quindi eseguendo un comando wait che attenderà il comando dell'ultimo funzionamento, rsync, finché non è completo.

Allora cerchiamo l'intero script con il trap '' SIGINT SIGTERM EXIT.

Riferimenti

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top