Bash не перехватывает прерывания во время операторов exec rsync/subshell

StackOverflow https://stackoverflow.com//questions/9624947

Вопрос

Контекст:

У меня есть сценарий 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) во время завершения выполнения/подоболочки sleep Команда, ловушка работает должным образом, и я вижу «Очистка!» эхо и зарегистрировано.Если я прерву сценарий во время rsync команда, я вижу rsync закончить и написать 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.

Это было полезно?

Решение

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

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

Другие советы

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

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

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

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

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

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

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

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

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

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

Из экспериментов довольно ясно, что 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

Как это работает

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

Затем мы охраняем весь скрипт с помощью trap '' SIGINT SIGTERM EXIT.

Рекомендации

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top