Pregunta

Contexto:

Tengo un script bash que contiene un subshell y una trampa para la pseudoseñal EXIT, y no captura adecuadamente las interrupciones durante una rsync.He aquí un ejemplo:

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

La idea del guión es esta:la mayor parte de la lógica importante se ejecuta en una subcapa que se canaliza a través de tee y a un archivo de registro, para no tener que hacerlo tee cada línea de la lógica principal para registrarlo todo.Siempre que finalice el subshell, o el script se detenga por cualquier motivo (la pseudoseñal EXIT debería capturar todos estos casos), la trampa lo interceptará y ejecutará el cleanup() función y luego retire la trampa.El rsync y sleep Los comandos (el sueño es solo un ejemplo) se ejecutan. exec para evitar la creación de procesos zombies si elimino el script principal mientras se están ejecutando, y cada comando potencialmente de larga duración está envuelto en su propio subshell para que cuando exec termina, no finalizará todo el script.

El problema:

Si interrumpo el guión (a través de kill o CTRL+C) durante el exec/subshell ajustado sleep Comando, la trampa funciona correctamente y veo "¡Limpieza!" resonado y registrado.Si interrumpo el guión durante el rsync comando, ya veo rsync terminar y escribir rsync error: received SIGINT, SIGTERM, or SIGHUP (code 20) at rsync.c(544) [sender=3.0.6] a la pantalla, y luego el guión simplemente muere;sin limpieza, sin trampas.¿Por qué no interrumpir/matar a rsync activar la trampa?

He intentado usar el --no-detach cambie con rsync, pero no cambió nada.Tengo bash 4.1.2, rsync 3.0.6, centOS 6.2.

¿Fue útil?

Solución

¿Qué tal si redirigimos toda la salida del punto X al tee sin tener que repetirla en todas partes y meternos con todos los sub-shells y ejecutivos...(espero no haberme perdido nada)

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

Otros consejos

Además de set -e, creo que quieres set -E:

Si se establece, cualquier trampa en ERR es heredada por funciones de shell, sustituciones de comandos y comandos ejecutados en un entorno de subshell.La trampa ERR normalmente no se hereda en tales casos.

Alternativamente, en lugar de envolver sus comandos en subshells, use llaves que aún le darán la capacidad de redirigir las salidas de los comandos pero las ejecutarán en el shell actual.

La interrupción se detectará correctamente si agrega INT a la trampa

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

Bash captura las interrupciones correctamente.Sin embargo, esto no responde a la pregunta de por qué el script se bloquea al salir si sleep se interrumpe, ni por qué no se activa rsync, pero hace que el script funcione como se supone que debe hacerlo.Espero que esto ayude.

Es posible que su shell esté configurado para salir en caso de error:

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

si interrumpes sleep (^C) entonces la subcapa saldrá debido a set -e e imprimir woah en el proceso.

Además, un poco sin relación:su trap - EXIT está en una subcapa (explícitamente), por lo que no tendrá efecto después de que regrese la función de limpieza

Está bastante claro a partir de la experimentación que rsync Se comporta como otras herramientas como ping y no hereda señales del padre Bash que llama.

Entonces debes ser un poco creativo con esto y hacer algo como lo siguiente:

$ cat rsync.bash
#!/bin/sh

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

 echo FIN

Ahora cuando lo ejecuto:

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

Y podemos ver que el archivo se transfirió por completo:

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

Cómo funciona

El truco aquí es que le decimos a Bash a través de set -m para deshabilitar los controles de trabajo en cualquier trabajo en segundo plano dentro de él.Luego estamos poniendo en segundo plano el rsync y luego ejecutando un wait comando que esperará el último comando de ejecución, rsync, hasta completarlo.

Luego guardamos todo el guión con el trap '' SIGINT SIGTERM EXIT.

Referencias

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top