Вопрос: Что делает «set -e», и почему это может считаться опасным?


Этот вопрос появился на предвыборной викторине, и это заставляет меня сходить с ума. Может ли кто-нибудь ответить на это и успокоить меня? В викторине нет ссылки на конкретную оболочку, но описание должно выполняться для unix sa. опять вопрос просто ...

Что делает «set -e», и почему это может считаться опасным?


134
2018-05-19 16:39


Источник


Вот связанный поток: stackoverflow.com/questions/64786/error-handling-in-bash/... - Stefan Lasiewski


Ответы:


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 небезопасны для вызова с установленным значением -e. Для скриптов init.d часто проще не использовать set -e и вместо этого проверять результат каждой команды отдельно.

Это правильный вопрос с точки зрения интервьюера, поскольку он оценивает кандидатов, которые изучают серверные сценарии и автоматизацию


138
2017-08-09 21:01



хорошая находка. Благодарю. - egorgry
хорошая точка зрения. он остановит процесс загрузки над чем-то, что может быть ошибкой технически, но не должно приводить к остановке всего сценария - Matt
Хотя я согласен с stackoverflow.com/questions/78497/..., Я думаю, что этот стиль находит хорошее соответствие с bash для коротких проверок инициализации и регистрации или других исправлений обезьяны окружающей среды перед выполнением целевой рабочей нагрузки. Мы используем эту же политику для наших сценариев инициализации образа докеров. - joshperry


Из 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.

К сожалению, я недостаточно творческий, чтобы думать о том, почему это было бы опасно, кроме «остальная часть скрипта не будет выполнена» или «возможно, возможно, замаскирует реальные проблемы».


32
2018-05-19 16:43



маскировать реальные / другие проблемы, да, я думаю, я мог видеть это ... я изо всех сил пытался придумать пример того, что это опасно, а также ... - cpbills
Есть справочная страница для set! Я всегда оказываюсь в builtin(1), Благодарю. - Jared Beck
как я узнаю, что документация для set должно быть найдено в man 1 bash?? и как кто-нибудь сможет найти -e вариант для set ключевое слово на странице руководства, которая так огромна? вы не можете просто /-e поищи здесь - Blauhirn
@Blauhirn, используя help set - Blauhirn


Необходимо отметить, что 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

Также следует отметить это из раздела страницы страницы Bash на trap команда, которая также описывает, как set -e функционирует при определенных обстоятельствах.

                Ловушка ERR не выполняется, если неудавшаяся команда является частью                 список команд сразу после некоторого времени или до ключевого слова,                 часть теста в выражении if, часть выполняемой команды                 в списке && или,, или если возвращаемое значение команды                 перевернутый через!. Это те же условия, которым                 errexit.

Таким образом, существуют некоторые условия, при которых ненулевой статус не приведет к выходу.

Я думаю, что опасность не понимает, когда set -e вступает в игру, а когда он этого не делает, и полагается на него неправильно при некоторых недопустимых предположениях.

Также см. BashFAQ / 105 Почему не задано -e (или set -o errexit или trap ERR) делать то, что я ожидал?


17
2018-05-19 22:39



Отклоняясь от вопроса немного, этот ответ именно то, что я ищу: отключение его для небольшой части скрипта! - Stephan Bijzitter


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

Вполне возможно, что «set -e» ничего не делает, что мы будем считать «опасным». Но автор этого вопроса может ошибочно полагать, что «set -e» опасен из-за их собственного невежества или предвзятости. Возможно, они написали сценарий с багги-оболочкой, он бомбил ужасно, и они ошибочно считали, что «set -e» виноват, когда на самом деле они пренебрегли написанием правильной проверки ошибок.

За последние 2 года я участвовал, вероятно, в 40 собеседованиях, а интервьюеры иногда задают неправильные вопросы или имеют неправильные ответы.

Или, может быть, это трюк, который был бы хромым, но не совсем неожиданным.

Или, может быть, это хорошее объяснение: http://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg473314.html


13
2018-05-19 17:03



+1 Я в одной лодке, и многие «технические» интервью, с которыми я имел дело, были, по крайней мере, слегка ошибочными. - cpbills
Вау. Хорошая находка в списке debian. +1 за незнание вопросов интервью. Я помню, как спорил с ответом netback, так как я был на 100% уверен, что я прав. Они сказали нет. Я пошел домой и пошел в нее, я был прав. - egorgry


set -e говорит bash, в скрипте, для выхода, когда что-либо возвращает ненулевое возвращаемое значение.

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


9
2018-05-19 16:46



Это интересно. Я не думал о разрешениях, открытых в сценарии, который умирает преждевременно, но я мог видеть, что это считается опасным. Интересной частью этой викторины является то, что вы не можете использовать какие-либо справочные материалы, такие как человек или Google, и если вы не можете ответить полностью, не отвечайте на него вообще. - egorgry
это просто глупо, я бы пересмотрел этот работодатель ... шучу (вроде). хорошо иметь сильную базу знаний, но половина ИТ-работы знала / где / находила информацию ... надеюсь, они были достаточно умны, чтобы принять это во внимание при подсчете претендентов. на боковой ноте, я вижу / нет / использую для set -e, на самом деле, для меня это говорит о лени, чтобы игнорировать проверку ошибок в ваших сценариях ... так что имейте это в виду, с этим работодателем тоже ... если они используют его много, как выглядят их сценарии, что вы неизбежно придется поддерживать ... - cpbills
отличная точка @cpbills. Я также не вижу смысла использовать set -e в скрипте, и, вероятно, поэтому он так сильно меня насторожил. Я бы хотел опубликовать все вопросы, так как некоторые из них действительно хороши, а некоторые действительно сумасшедшие и случайные, как этот. - egorgry
Я видел это во многих рабочих задачах cron ... - Andrew


set -e завершает сценарий, если встречается ненулевой код выхода, за исключением определенных условий. Подводя итог опасностям его использования в нескольких словах: он не ведет себя так, как думают люди.

На мой взгляд, это следует рассматривать как опасный взлом, который продолжает существовать только для целей совместимости. set -e оператор не превращает оболочку из языка, который использует коды ошибок на язык, который использует поток управления, подобный исключению, он просто плохо пытается подражать этому поведению.

Грэгу Вулэджу есть что сказать об опасностях set -e:

Во втором звене есть различные примеры неинтуитивного и непредсказуемого поведения set -e,

Некоторые примеры неинтуитивного поведения set -e (некоторые взяты из ссылки wiki выше):

  • set -e
    х = 0
    пусть x ++
    echo "x равно $ x"
    Вышеизложенное приведет к преждевременному завершению сценария оболочки, поскольку let x++ возвращает 0, который обрабатывается let ключевое слово как значение фальшивки и превращено в ненулевой код выхода. set -e замечает это и молча закрывает скрипт.
  • set -e
    [-d / opt / foo] && echo «Предупреждение: foo уже установлен. Будет перезаписан». > & 2
    echo "Установка foo ..."
    

    Вышеупомянутое работает, как и ожидалось, печатая предупреждение, если /opt/foo уже существует.

    set -e
    check_previous_install () {
        [-d / opt / foo] && echo «Предупреждение: foo уже установлен. Будет перезаписан». > & 2
    }
    
    check_previous_install
    echo "Установка foo ..."
    

    Вышеизложенное, несмотря на единственную разницу в том, что одна строка была реорганизована в функцию, прекратится, если /opt/foo не существует. Это связано с тем, что тот факт, что он работал первоначально, является особым исключением для set -eПоведение. когда a && b возвращает ненулевое значение, оно игнорируется set -e, Однако теперь, когда это функция, код выхода этой функции равен коду выхода этой команды, а функция, возвращающая ненулевое значение, будет бесшумно завершать скрипт.

  • set -e
    IFS = $ '\ n' read -d '' -r -a config_vars <config
    

    Вышеприведенное будет читать массив config_vars из файла config, Как мог бы предположить автор, он завершается с ошибкой if config пропал, отсутствует. Поскольку автор может не намереваться, он молча прекращается, если config не заканчивается символом новой строки. Если set -e здесь не использовались, тогда config_vars будет содержать все строки файла независимо от того, закончилась ли она в новой строке.

    Пользователи Sublime Text (и другие текстовые редакторы, которые обрабатывают новые строки неправильно), остерегайтесь.

  • set -e
    
    should_audit_user () {
        группы локальных групп = "$ (группы" $ 1 ")"
        для группы в $ группах; делать
            if ["$ group" = audit]; затем верните 0; фи
        сделанный
        возвращение 1
    }
    
    if should_audit_user "$ user"; тогда
        logger 'Blah'
    фи
    

    Автор здесь может разумно ожидать, что если по какой-то причине пользователь $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- условная точка.
  • Места, где оболочка заканчивается, несовместимы между оболочками или версиями оболочки. Можно случайно написать скрипт, который по-разному ведет себя на более старой версии bash из-за поведения set -e были изменены между этими версиями.

set -e это спорная проблема, и некоторые люди, осведомленные о проблемах, связанных с ней, рекомендуют против нее, в то время как некоторые другие просто рекомендуют проявлять осторожность, пока она активна, чтобы знать подводные камни. Есть много новичков сценариев для скриптов, которые рекомендуют set -eна всех сценариях как на уловку для ошибок, но в реальной жизни это не работает.

set -e не является заменой образования.


5
2018-04-27 23:06





Я бы сказал, что это опасно, потому что вы больше не контролируете поток своего сценария. Сценарий может завершиться до тех пор, пока любая из команд, вызываемых скриптом, возвращает ненулевое значение. Таким образом, все, что вам нужно сделать, это сделать что-то, что изменяет поведение или выход любого из компонентов, и вы можете завершить основной скрипт. Это может быть скорее проблема стиля, но это определенно имеет последствия. Что, если ваш основной сценарий должен установить некоторый флаг, а это не потому, что он закончился раньше? В конечном итоге вы нарушите всю систему, если предполагается, что флаг должен быть там, или работать с неожиданным значением по умолчанию или старым значением.


2
2018-05-19 18:07



Полностью согласен с этим ответом. Это не по существу опасно, но это возможность иметь неожиданный ранний выход. - pboin
То, что это напомнило мне, - это мой профи С ++, который всегда вытягивал очки из моих назначений за то, что у меня не было одной точки входа / единственной точки выхода в программе. Я думал, что это чисто «принцип», но этот набор - это определенно демо, как и почему все может полностью вырваться из-под контроля. В конечном счете, программы касаются контроля и детерминизма, и с преждевременным прекращением вы отказываетесь от обоих. - Marcin


Как более конкретный пример ответа @ Marcin, который лично укусил меня, представьте, что rm foo/bar.txt строка где-то в вашем скрипте. Обычно нет большой сделки, если foo/bar.txt фактически не существует. Однако с set -e сейчас ваш скрипт закончится раньше! К сожалению.


0
2018-06-22 19:00