Если я использую, trap
как описано, например, на http://linuxcommand.org/wss0160.php#trap, чтобы перехватить ctrl-c (или подобный) и выполнить очистку перед выходом, то я изменяю возвращенный код выхода.
Теперь это, вероятно, не будет иметь значения в реальном мире (например, потому что коды выхода не являются переносимыми и вдобавок к этому не всегда однозначными, как обсуждалось в Коде выхода по умолчанию, когда процесс завершается? ), Но все же мне интересно, есть ли там неужели нет способа предотвратить это и вместо этого вернуть код ошибки по умолчанию для прерванных сценариев?
Пример (в bash, но мой вопрос не должен рассматриваться как специфичный для bash):
#!/bin/bash
trap 'echo EXIT;' EXIT
read -p 'If you ctrl-c me now my return code will be the default for SIGTERM. ' _
trap 'echo SIGINT; exit 1;' INT
read -p 'If you ctrl-c me now my return code will be 1. ' _
Вывод:
$ ./test.sh # doing ctrl-c for 1st read
If you ctrl-c me now my return code will be the default for SIGTERM.
$ echo $?
130
$ ./test.sh # doing ctrl-c for 2nd read
If you ctrl-c me now my return code will be the default for SIGTERM.
If you ctrl-c me now my return code will be 1. SIGINT
EXIT
$ echo $?
1
(Отредактировано для удаления, чтобы сделать его более соответствующим POSIX.)
(Вместо этого отредактировано снова, чтобы сделать его bash-скриптом, но мой вопрос не относится к оболочке.)
Отредактировано для использования переносного "INT" для ловушки в пользу непереносимого "SIGINT".
Отредактировано, чтобы удалить ненужные фигурные скобки и добавить потенциальное решение.
Обновить:
Я решил это сейчас, просто выйдя с жестко закодированными кодами ошибок и перехватив EXIT. Это может быть проблематично в некоторых системах, потому что код ошибки может отличаться или прерывание EXIT невозможно, но в моем случае это достаточно.
trap cleanup EXIT
trap 'exit 129' HUP
trap 'exit 130' INT
trap 'exit 143' TERM
read
чтобы читать из текущего сопроцессора иtrap cmd SIGINT
не будет работать, поскольку стандарт говорит, что вы должны использоватьtrap cmd INT
.read -p
читает входные данные текущего процесса.Ответы:
Все, что вам нужно сделать, это изменить обработчик EXIT внутри вашего обработчика очистки. Вот пример:
источник
trap cleanup INT
вместоtrap cleanup EXIT
?На самом деле прерывание внутренней части bash,
read
похоже, немного отличается от прерывания команды, запускаемой bash. Обычно, когда вы входитеtrap
,$?
установлен , и вы можете сохранить его и выйти с тем же значением:Если ваш сценарий прерывается при выполнении команды like
sleep
или встроенной подобнойwait
, вы увидитеи код выхода равен 130. Однако
read -p
, похоже, он$?
равен 0 (в любом случае, в моей версии bash 4.3.42).Обработка сигналов во время
read
может быть незавершенной, в соответствии с файлом изменений в моей версии ... (/ usr / share / doc / bash / CHANGES)источник
read
, как отмечено в файле CHANGES (добавлено в мой ответ). Таким образом, это может быть работа в процессе.128 + signo
в качестве кода выхода для сигналов, ksh93 использует256 + signo
. POSIX говорит: что-то выше 128 ....Любой обычный код выхода сигнала будет доступен
$?
при входе в обработчик ловушек:Если есть отдельная ловушка EXIT, вы можете использовать ту же методологию: немедленно сохранить состояние выхода, передаваемое обработчиком сигнала (если он есть), очищенным, а затем вернуть сохраненное состояние выхода.
источник
local
не POSIX, хотя{ba,z}sh
. AFAIK, это лучшее, что может быть сделано, независимо от.$?
равно 1 независимо от полученного сигнала).Просто вернуть код ошибки недостаточно для имитации выхода с помощью SIGINT. Я удивлен, что никто не упомянул это до сих пор. Дополнительная информация: https://www.cons.org/cracauer/sigint.html.
Правильный способ это:
Это работает с Bash, Dash и Zsh. Для дальнейшей переносимости вам нужно использовать числовые спецификации сигналов (с другой стороны, zsh ожидает строковый параметр для
kill
команды ...)Также обратите внимание на специальную обработку
EXIT
сигнала. Это происходит из-за того, что некоторые оболочки (а именно Bash) выполняют ловушкиEXIT
для любого сигнала (после, возможно, определенных ловушек для этого сигнала). СбросEXIT
ловушки предотвращает это.Выполнение кода только при «нормальном» выходе
Проверка
[ $sig = EXIT ]
позволяет выполнять код только при нормальном (не сигнализированном) выходе. Тем не менее, все сигналы должны иметь ловушки, которые, наконец, сбрасывают ловушку наEXIT
потом;normal_exit_only_cleanup
также будет вызываться для сигналов, которые не делают. Это также будет выполнено выходом черезset -e
. Это можно исправить путем захватаERR
(который не поддерживается Dash) и добавления проверки[ $sig = ERR ]
передkill
.Упрощенная версия только для Bash
С другой стороны, это поведение означает, что в Bash вы можете просто сделать
просто выполнить некоторый код очистки и сохранить статус выхода.
отредактированный
Разработать поведение Баша "EXIT заманивает все в ловушку"
Удалить сигнал KILL, который не может быть пойман в ловушку
Удалить префиксы SIG из имен сигналов
Не пытайтесь
kill -s EXIT
Принять
set -e
/ ERR во вниманиеисточник
SIGINT
является способом ее закрытия, и она может решить выйти с 0, если ей удалось завершить работу без ошибки, или с другим кодом ошибки, если она не завершилась корректно. Дело в точке:top
. Запустите,top; echo $?
а затем нажмите Ctrl-C. Статус, сброшенный на экран, будет 0.