Bash не перехватывает прерывания во время операторов rsync / subshell exec - PullRequest
10 голосов
/ 09 марта 2012

Контекст:

У меня есть bash-скрипт, который содержит подоболочку и ловушку для псевдосигнала EXIT, и он неправильно перехватывает прерывания во время rsync. Вот пример:

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

Идея сценария такова: большая часть важной логики выполняется в подоболочке, которая передается по каналу tee и в лог-файл, поэтому мне не нужно tee каждую строку основной логики получить все это в журнале. Всякий раз, когда подоболочка заканчивается или сценарий останавливается по какой-либо причине (псевдосигнал EXIT должен охватывать все эти случаи), ловушка перехватит ее и запустит функцию cleanup(), а затем удалит ловушку. Команды rsync и sleep (спящий режим - всего лишь пример) запускаются через exec, чтобы предотвратить создание процессов-зомби, если я убиваю родительский скрипт во время их выполнения, и каждую потенциально длительную команду обернут в свою собственную подоболочку, поэтому, когда exec завершится, он не завершит весь сценарий.

Проблема:

Если я прерываю сценарий (через kill или CTRL + C) во время выполнения команды exec / subshell wrapped sleep, ловушка работает правильно, и я вижу сообщение "Очистка!" повторил и вошел. Если я прерываю сценарий во время команды rsync, я вижу rsync end и записываю rsync error: received SIGINT, SIGTERM, or SIGHUP (code 20) at rsync.c(544) [sender=3.0.6] на экран, а затем сценарий просто умирает; без очистки, без ловушек. Почему прерывание / убийство rsync не вызывает ловушку?

Я пытался использовать переключатель --no-detach с rsync, но это ничего не изменило. У меня bash 4.1.2, rsync 3.0.6, centOS 6.2.

Ответы [ 5 ]

2 голосов
/ 04 июня 2012

Как насчет того, чтобы все выходные данные из точки X были перенаправлены на тройник без необходимости повторять его повсюду и связываться со всеми вложенными оболочками и руководителями ... (надеюсь, я что-то не пропустил)

#!/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
2 голосов
/ 04 июня 2012

В дополнение к set -e, я думаю, вы хотите set -E:

Если установлено, любая ловушка в ERR наследуется функциями оболочки, подстановками команд и командами, выполняемыми в подчиненном элементе.оболочка среды.Ловушка ERR обычно не наследуется в таких случаях.

В качестве альтернативы, вместо того, чтобы обернуть ваши команды в подоболочки, используйте фигурные скобки, которые все равно дадут вам возможность перенаправлять выходные команды, но будут выполнять их в текущем режиме.оболочки.

1 голос
/ 16 марта 2012

Прерывание будет правильно перехвачено, если вы добавите INT в ловушку

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

Bash правильно прерывает прерывания.Однако это не отвечает на вопрос, почему скрипт прерывается при выходе, если sleep прерывается, и почему он не запускается на rsync, но заставляет скрипт работать так, как он должен.Надеюсь, это поможет.

0 голосов
/ 10 июля 2018

Из экспериментов довольно ясно, что rsync ведет себя как другие инструменты, такие как ping, и не наследует сигналы от вызывающего родителя Bash.

Так что вам нужно проявить немного творчества и сделатьчто-то вроде следующего:

$ cat rsync.bash
#!/bin/sh

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

 echo FIN

Теперь, когда я его запустил:

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

И мы видим, что файл полностью перешел:

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

Как этоworks

Хитрость в том, что мы говорим Bash через set -m отключить элементы управления заданиями для любых фоновых заданий в нем.Затем мы выполняем фоновый режим rsync, а затем запускаем команду wait, которая будет ждать выполнения последней команды запуска rsync до ее завершения.

Затем мы защищаем весь сценарий с помощью trap '' SIGINT SIGTERM EXIT.

Ссылки

0 голосов
/ 16 марта 2012

Ваша оболочка может быть настроена на выход при ошибке:

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

Если вы прервете sleep (^ C), то подоболочка выйдет из-за set -e и напечатает woah в процессе,

Кроме того, немного не связано: ваш trap - EXIT находится в подоболочке (явно), поэтому он не будет иметь эффекта после того, как функция очистки вернет

...