Подтвердить что ты не робот

Правильный способ обнаружения кода выхода оболочки при установке опции errexit

Я предпочитаю писать сплошную оболочку, поэтому набор errexit и nounset всегда установлен.

Следующий код будет остановлен в строке bad_command

#!/bin/bash
set -o errexit ; set -o nounset
bad_command # stop here
good_command

Я хочу его захватить, вот мой метод

#!/bin/bash
set -o errexit ; set -o nounset
rc=1
bad_command && rc=0 # stop here
[ $rc -ne 0 ] && do_err_handle
good_command

Есть ли лучший или более чистый метод

Мой ответ:

#!/bin/bash
set -o errexit ; set -o nounset
if ! bad_command ; then
  # error handle here
fi
good_command
4b9b3361

Ответ 1

Как насчет этого? Если вы хотите получить код выхода...

#!/bin/sh                                                                       
set -e

cat /tmp/doesnotexist && rc=$? || rc=$?                                         
echo exitcode: $rc        

cat /dev/null && rc=$? || rc=$?                                                 
echo exitcode: $rc   

Вывод:

cat: /tmp/doesnotexist: No such file or directory
exitcode: 1
exitcode: 0

Ответ 2

Хранить с errexit. Это может помочь найти ошибки, которые в противном случае могли бы иметь непредсказуемые (и трудно обнаруживаемые) результаты.

#!/bin/bash
set -o errexit ; set -o nounset

bad_command || do_err_handle
good_command

Вышеуказанное будет работать нормально. errexit требуется только, чтобы линия проходила, как и в случае с bad_command && rc=0. Поэтому выше с помощью "или" будет выполняться только do_err_handle, если bad_command завершается сбой, и пока do_err_handle не "сбой", то script будет продолжен.

Ответ 3

set -o errexit

bad_command || {
  resp_code=$?
  echo Bad Thing $resp_code happened
}

Ответ 4

Согласитесь с комментариями, поэтому, если вы можете отказаться от errexit, вы можете легко сократить код до

 bad_command || do_err_handle
 good_command

Надеюсь, это поможет.

Ответ 5

Продолжайте писать твердый код оболочки.

Вы делаете это правильно, если bad_command действительно является командой. Но будьте осторожны с вызовами функций в if, while, ||, && или!, потому что errexit там не работает. Это может быть опасно.

Если ваша bad_command действительно bad_function, вы должны написать это:

set -eu 

get_exit_code() {
    set +e
    ( set -e;
      "[email protected]"
    )
    exit_code=$?
    set -e
}

...

get_exit_code bad_function

if [ "$exit_code" != 0 ]; then
    do_err_handle
fi

Это хорошо работает в bash 4.0. В bash 4.2 вы получаете только коды выхода 0 или 1.

Ответ 6

Обычный способ избежать выхода из программы Bash, когда errexit установлен, а команда, которая может выйти из строя, запускается, должна предшествовать команде с !.

После выполнения команды $? не содержит статуса выхода команды. (Он содержит 0, если команда не выполнена, и 1 в противном случае.) Однако массив PIPESTATUS содержит статус выхода команды. Безопасным способом захвата статуса выхода команды, которая может выйти из строя, независимо от того, установлен ли или нет errexit, является:

! bad_command
rc=${PIPESTATUS[0]}

Вторая строка может быть упрощена до rc=$PIPESTATUS, но Shellcheck будет жаловаться на нее.

Если (как это часто бывает), вам не нужно знать статус выхода команды, только если это удалось или не удалось, тогда решение @george хорошо, если обработчик ошибок является однострочным. Для многострочных обработчиков ошибок хороший вариант:

if ! bad_command ; then
    # Handle errors here
fi

Обратите внимание, что (кроме очень необычных обстоятельств) было бы не корректно использовать `bad_command` (как предложено в вопросе) вместо простого bad_command. Вы можете использовать ${PIPESTATUS[0]}, чтобы получить статус завершения команды, если это необходимо в коде обработки ошибок, поскольку $? также не содержит его в этом случае.

Ответ 7

что, если вы хотите узнать статус выхода bad_command?

Я думаю, что самый простой способ - отключить errexit:

#!/bin/sh
set -o errexit

some_code_here

set +o errexit
bad_command
status=$?
set -o errexit
process $status

Ответ 9

Я собрал вместе (надеюсь) учебник из всех ответов:

#!/usr/bin/env bash

# exit immediately on error
set -o errexit

file='dfkjlfdlkj'
# Turn off 'exit immediately on error' during the command substitution
blah=$(set +o errexit && ls $file) && rc=$? || rc=$?
echo $blah
# Do something special if $rc
(( $rc )) && echo failure && exit 1
echo success

Ответ 10

Небольшая вариация ответа от @rrauenza. Так как && rc=$? Частично, их ответ всегда будет равен && rc=0 Можно также установить rc в 0 перед запуском команды. На мой взгляд, результат оказывается более читабельным, потому что переменная определяется заранее в собственной строке кода и изменяется только в том случае, если команда завершается с ненулевым состоянием выхода. Если также указано имя nounset, то теперь ясно, что rc действительно никогда не был неопределенным. Это также позволяет избежать смешивания && и || в той же строке, что может сбивать с толку, потому что не всегда можно знать приоритет оператора наизусть.

#!/bin/sh                                                                       
set -eu

rc=0
cat /tmp/doesnotexist || rc=$?                                         
echo exitcode: $rc        

rc=0
cat /dev/null || rc=$?                                                 
echo exitcode: $rc   

Выход:

cat: /tmp/doesnotexist: No such file or directory
exitcode: 1
exitcode: 0

Ответ 11

Чистый надежный способ выхода из ошибки

command_that_error_exits || { echo "Line $LINENO: Failed with Error" 1>&2; exit 1;}

Ответ 12

Принятый ответ хорош, но я думаю, что он может быть реорганизован, чтобы быть еще лучше; более общий, проще рефакторинг и читать:

some_command_status=$(some_command && echo $? || echo $?)

против.

some_command && some_command_status=$? || some_command_status=$?