Этот вопрос появился в викторине перед собеседованием, и это сводит меня с ума. Может ли кто-нибудь ответить на этот вопрос и успокоить меня? В викторине нет ссылки на конкретную оболочку, но описание работы предназначено для unix sa. опять вопрос просто ...
Что делает set -e и почему это можно считать опасным?
set -e
вызывает завершение работы оболочки, если какая-либо подкоманда или конвейер возвращает ненулевое состояние.
Ответ, вероятно, искал интервьюер:
Было бы опасно использовать "set -e" при создании скриптов init.d:
Из http://www.debian.org/doc/debian-policy/ch-opersys.html 9.3.2 -
Будьте осторожны при использовании set -e в сценариях init.d. Написание правильных сценариев init.d требует принятия различных статусов выхода из ошибок, когда демоны уже запущены или уже остановлены, без прерывания сценария init.d, а общие библиотеки функций init.d небезопасно вызывать с помощью set -e в действии. Для сценариев init.d часто проще не использовать set -e и вместо этого проверять результат каждой команды отдельно.
Это верный вопрос с точки зрения интервьюера, поскольку он позволяет оценить, насколько хорошо кандидат знает сценарии и автоматизацию на уровне сервера.
Из bash(1)
:
-e Exit immediately if a pipeline (which may consist of a
single simple command), a subshell command enclosed in
parentheses, or one of the commands executed as part of
a command list enclosed by braces (see SHELL GRAMMAR
above) exits with a non-zero status. The shell does not
exit if the command that fails is part of the command
list immediately following a while or until keyword,
part of the test following the if or elif reserved
words, part of any command executed in a && or ││ list
except the command following the final && or ││, any
command in a pipeline but the last, or if the command’s
return value is being inverted with !. A trap on ERR,
if set, is executed before the shell exits. This option
applies to the shell environment and each subshell envi-
ronment separately (see COMMAND EXECUTION ENVIRONMENT
above), and may cause subshells to exit before executing
all the commands in the subshell.
К сожалению, я недостаточно изобретателен, чтобы думать о том, почему это было бы опасно, кроме «остальная часть скрипта не будет выполнена» или «возможно, это может скрыть реальные проблемы».
Следует отметить, что set -e
можно включать и выключать для различных разделов скрипта. Он не обязательно должен быть включен для всего выполнения скрипта. Его даже можно было включить условно. Тем не менее, я никогда не использую его, так как я занимаюсь собственной обработкой ошибок (или нет).
some code
set -e # same as set -o errexit
more code # exit on non-zero status
set +e # same as set +o errexit
more code # no exit on non-zero status
Также следует отметить, что это из раздела man-страницы Bash на trap
команда, которая также описывает, как set -e
функционирует при определенных обстоятельствах.
Ловушка ERR не выполняется, если неудавшаяся команда является частью списка команд сразу после ключевого слова while или until, частью теста в операторе if, частью команды, выполняемой в списке && или ⎪⎪, или если команда возвращаемое значение инвертируется через!. Это те же условия, которым подчиняется опция errexit.
Итак, есть некоторые условия, при которых ненулевой статус не приведет к выходу.
Я думаю, опасность в том, чтобы не понимать, когда set -e
вступает в игру, а когда нет, и неверно полагается на него при каком-то неверном предположении.
Пожалуйста, также смотрите BashFAQ / 105 Почему set -e (или set -o errexit, или trap ERR) не делает того, что я ожидал?
Имейте в виду, что это тест для собеседования. Вопросы могли быть написаны нынешним персоналом, и они могут быть неправильными. Это не обязательно плохо, и все делают ошибки, а вопросы на собеседовании часто остаются в темном углу без рассмотрения и всплывают только во время собеседования.
Вполне возможно, что set -e не делает ничего, что мы сочли бы «опасным». Но автор этого вопроса может ошибочно полагать, что «set -e» опасно из-за своего собственного невежества или предвзятости. Возможно, они написали скрипт оболочки с ошибками, он ужасно провалился, и они ошибочно думали, что виноват «set -e», хотя на самом деле они пренебрегли написанием надлежащей проверки ошибок.
Я участвовал в примерно 40 собеседованиях за последние 2 года, и интервьюеры иногда задают неправильные вопросы или имеют неправильные ответы.
Или, может быть, это вопрос с подвохом, который был бы неубедительным, но не совсем неожиданным.
Или, может быть, это хорошее объяснение: http://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg473314.html
set -e
сообщает bash в сценарии о завершении работы, когда что-либо возвращает ненулевое возвращаемое значение.
я мог видеть, как это будет раздражать и глючить, не уверен в опасности, если вы не открыли разрешения на что-то, и прежде чем вы смогли их снова ограничить, ваш скрипт умер.
set -e
завершает скрипт, если встречается ненулевой код выхода, за исключением определенных условий. Подводя итог опасностям его использования в нескольких словах: он ведет себя не так, как люди думают.
На мой взгляд, это следует рассматривать как опасный взлом, который продолжает существовать только в целях совместимости. В set -e
Оператор не превращает оболочку из языка, использующего коды ошибок, в язык, использующий поток управления, подобный исключениям, он просто плохо пытается имитировать такое поведение.
Грег Вулдж может многое сказать об опасностях set -e
:
set -e
)Во второй ссылке представлены различные примеры неинтуитивного и непредсказуемого поведения set -e
.
Некоторые примеры неинтуитивного поведения set -e
(некоторые взяты из вики-ссылки выше):
set -e x=0 let x++ echo "x is $x"Вышеуказанное приведет к преждевременному завершению работы сценария оболочки, потому что
let x++
возвращает 0, что обрабатывается let
ключевое слово как ложное значение и превратилось в ненулевой код выхода. set -e
замечает это и молча завершает сценарий.set -e [ -d /opt/foo ] && echo "Warning: foo is already installed. Will overwrite." >&2 echo "Installing foo..."
Вышеуказанное работает, как ожидалось, выводит предупреждение, если /opt/foo
уже существует.
set -e check_previous_install() { [ -d /opt/foo ] && echo "Warning: foo is already installed. Will overwrite." >&2 } check_previous_install echo "Installing foo..."
Вышеупомянутое, несмотря на единственную разницу в том, что одна строка была преобразована в функцию, завершится, если /opt/foo
не существует. Это потому, что тот факт, что он работал изначально, является особым исключением из set -e
поведение. когда a && b
возвращает ненулевое значение, игнорируется set -e
. Однако теперь, когда это функция, код выхода функции равен коду выхода этой команды, и функция, возвращающая ненулевое значение, молча завершит скрипт.
set -e IFS=$'\n' read -d '' -r -a config_vars < config
Вышеупомянутое будет читать массив config_vars
из файла config
. По замыслу автора, он завершается ошибкой, если config
пропал, отсутствует. Поскольку автор может не предполагать, он молча прекращается, если config
не заканчивается новой строкой. Если set -e
здесь не использовались, то config_vars
будет содержать все строки файла независимо от того, заканчивался он новой строкой или нет.
Остерегайтесь пользователей Sublime Text (и других текстовых редакторов, которые неправильно обрабатывают символы новой строки).
set -e should_audit_user() { local group groups="$(groups "$1")" for group in $groups; do if [ "$group" = audit ]; then return 0; fi done return 1 } if should_audit_user "$user"; then logger 'Blah' fi
Автор здесь может разумно ожидать, что если по какой-то причине пользователь $user
не существует, то groups
команда завершится ошибкой, и сценарий завершится, вместо того, чтобы позволить пользователю выполнить некоторую задачу без аудита. Однако в этом случае set -e
прекращение действия никогда не вступает в силу. Если $user
не может быть найден по какой-то причине, вместо завершения скрипта should_audit_user
функция просто вернет неверные данные, как если бы set -e
не действовал.
Это применимо к любой функции, вызываемой из условной части if
оператор, независимо от того, насколько глубоко вложен, независимо от того, где он определен, даже если вы запустите set -e
снова внутри него. С помощью if
в любой момент полностью отключает эффект set -e
пока блок условия не будет полностью выполнен. Если автор не знает об этой ловушке или не знает всего своего стека вызовов во всех возможных ситуациях, в которых может быть вызвана функция, тогда он напишет код с ошибками и ложное чувство безопасности, обеспечиваемое set -e
будет хотя бы частично виноват.
Даже если автор полностью осознает эту ловушку, обходной путь состоит в том, чтобы писать код так же, как если бы он был написан без set -e
, эффективно делая этот переключатель менее чем бесполезным; не только автору приходится писать код обработки ошибок вручную, как будто set -e
не действовали, но наличие set -e
возможно, обманули их, заставив думать, что они не должны этого делать.
Некоторые дополнительные недостатки set -e
:
let x++
выше, это не так. Если сценарий неожиданно умирает, обычно происходит тихо, что затрудняет отладку. Если сценарий не умирает, и вы ожидали, что это произойдет (см. Предыдущий пункт), значит, в ваших руках более тонкая и коварная ошибка.if
-условие маркированного списка.set -e
были изменены между этими версиями.set -e
является спорным вопросом, и некоторые люди, осведомленные о проблемах, связанных с ним, рекомендуют его не использовать, в то время как другие просто рекомендуют проявлять осторожность, пока он активен, чтобы знать подводные камни. Есть много новичков в написании сценариев оболочки, которые рекомендуют set -e
во всех сценариях как универсальное средство для ошибок, но в реальной жизни это не работает.
set -e
не может заменить образование.
Я бы сказал, что это опасно, потому что вы больше не контролируете ход своего сценария. Сценарий может завершиться до тех пор, пока любая из команд, которые он вызывает, возвращает ненулевое значение. Итак, все, что вам нужно сделать, это сделать что-то, что изменит поведение или вывод любого из компонентов, и вы получите возможность завершить основной сценарий. Возможно, это скорее проблема стиля, но определенно имеет последствия. Что, если ваш главный скрипт должен был установить какой-то флаг, но этого не произошло, потому что он преждевременно завершился? Вы закончите ошибкой остальную часть системы, если она предположит, что флаг должен быть там, или работаете с неожиданным значением по умолчанию или старым значением.
В качестве более конкретного примера ответа @Marcin, который лично меня укусил, представьте, что был rm foo/bar.txt
строка где-нибудь в вашем скрипте. Обычно ничего страшного, если foo/bar.txt
на самом деле не существует. Однако с set -e
представьте сейчас ваш скрипт завершится раньше срока! Ой.