BASH: использование команды trap для перехвата сигналов прерывания процесса

Автор: | 11/20/2013
 

terminalПро команду kill и сигналы управления процессами (SIGKILL, SIGTERM и другие) можно прочитать в статье Linux&FreeBSD: команды kill, nohup — сигналы и управление процессами.

У bash имеется встроенная команда trap, которая может перехватывать такие команды и выполнять какие-либо действия, заданные ей.

Синтаксис команды:

trap command signals

command — что именно необходимо выполнить при перехвате сигнала;
signals — список сигналов, которые необходимо перехватывать. Сигналы можно указывать как в полном виде — SIGTERM, так и в виде кода — 1, 2 и т.д.

Рассмотрим простой пример скрипта, в котором цикл будет выполняться до тех пор, пока не получит сигнал Ctrl+C (SIGINTinterrupt) или не достигнет значения 100 для переменной $count:

$ cat trap-1.sh
#!/bin/bash
count=0

trap 'echo "Exit"; exit 1' 2

while [ $count -lt 100 ]
do
sleep 1
(( count++ ))
echo $count
done

Про циклы читайте в статье BASH: описание циклов for, while, until и примеры использования.

Пример его выполнения:

$ ./trap-1.sh
1
2
3
Exit

После получения сигнала SIGINT (Ctrl+C) trap выполнил команду 'echo "Exit"' и сразу за ней — "exit 1", завершив работу скрипта. В этом примере сигнал, который надо перехватить задан в виде кода — SIGINT = 2 и т.д.

Список кодов наиболее используемых сигналов в таблице ниже:

Signal Name Signal Number Description
SIGHUP 1 Hang up detected on controlling terminal or death of controlling process
SIGINT 2 Issued if the user sends an interrupt signal (Ctrl + C).
SIGQUIT 3 Issued if the user sends a quit signal (Ctrl + D).
SIGFPE 8 Issued if an illegal mathematical operation is attempted
SIGKILL 9 If a process gets this signal it must quit immediately and will not perform any clean-up operations
SIGALRM 14 Alarm Clock signal (used for timers)
SIGTERM 15 Software termination signal (sent by kill by default).

Команде trap можно передавать список сигналов, разделённые пробелами:

trap 'echo "Exit"; exit 1' 1 2 3 15

В качестве команды trap может принимать функции. Возьмём пример функции answer и немного переделаем наш скрипт:

$ cat trap.sh
#!/bin/bash
count=0

answer () {
while read response; do
echo
case $response in
[yY][eE][sS]|[yY])
printf "$1n"
return 0
#$2
break
;;
[nN][oO]|[nN])
printf "$2n"
return 1
#$4
break
;;
*)
printf "Please, enter Y(yes) or N(no)! "
esac
done
}

trap 'printf "Are you sure to skip? [Y/n] "; answer && printf "nSkipping...nn" && exit 1 ' SIGINT

while [ $count -lt 100 ]
do
sleep 1
(( count++ ))
echo $count
done

Тут мы задали сигнал в виде имени — SIGINT. Посмотрим, как выполняется такой скрипт:

$ ./trap.sh
1
2

3
Are you sure to skip? [Y/n] n

4
5

6
Are you sure to skip? [Y/n] y

Skipping...

Таким же образом можно заставить скрипт игнорировать полученные сигналы (кроме kill -9 - "SIGKILL", который уничтожит процесс в любом случае). Для это вместо команды — просто укажите пустые одинарные кавычки — ''. Пример:

$ cat trap-2.sh
#!/bin/bash
count=0

trap '' 2

while [ $count -lt 100 ]
do
sleep 1
(( count++ ))
echo $count
done
$ ./trap-2.sh
1
2
3
...
18
19
20
Terminated

Процесс нельзя будет прервать комбинацией Ctrl+C, но он был прерван командой kill (SIGTERM, код 15 используется kill по-умолчанию).

Если же добавить код 15:

trap '' 2 15

То прервать выполнение можно будет только с помощью kill -9.