Сценарий выхода из оболочки на основе кода выхода процесса

StackOverflow https://stackoverflow.com/questions/90418

  •  01-07-2019
  •  | 
  •  

Вопрос

У меня есть сценарий оболочки, который выполняет ряд команд.Как завершить работу сценария оболочки, если какая-либо из команд завершается с ненулевым кодом выхода?

Это было полезно?

Решение

После каждой команды код выхода можно найти в $? переменная, чтобы у вас было что-то вроде:

ls -al file.ext
rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi

Вам следует быть осторожными с командами, передаваемыми по конвейеру, поскольку $? дает вам только код возврата последнего элемента в канале, поэтому в коде:

ls -al file.ext | sed 's/^/xx: /"

не вернет код ошибки, если файл не существует (поскольку sed часть конвейера действительно работает, возвращая 0).

А bash оболочка на самом деле предоставляет массив, который может помочь в этом случае, т.е. PIPESTATUS.Этот массив содержит по одному элементу для каждого компонента конвейера, к которому вы можете получить доступ индивидуально, например ${PIPESTATUS[0]}:

pax> false | true ; echo ${PIPESTATUS[0]}
1

Обратите внимание, что это дает вам результат false команда, а не весь конвейер.Вы также можете обработать весь список по своему усмотрению:

pax> false | true | false; echo ${PIPESTATUS[*]}
1 0 1

Если вы хотите получить самый большой код ошибки из конвейера, вы можете использовать что-то вроде:

true | true | false | true | false
rcs=${PIPESTATUS[*]}; rc=0; for i in ${rcs}; do rc=$(($i > $rc ? $i : $rc)); done
echo $rc

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

Другие советы

Если вы хотите работать с $?, вам нужно будет проверять его после каждой команды, поскольку $?обновляется после выхода каждой команды.Это означает, что если вы запустите конвейер, вы получите код завершения только последнего процесса в конвейере.

Другой подход заключается в следующем:

set -e
set -o pipefail

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

Видеть здесь или здесь для более подробного обсуждения этой проблемы. Здесь — это раздел руководства по bash во встроенном наборе.

"set -e", вероятно, самый простой способ сделать это.Просто поместите это перед любыми командами в вашей программе.

Если вы просто вызовете выход в bash без параметров, он вернет код выхода последней команды.В сочетании с OR bash должен вызывать выход только в том случае, если предыдущая команда не удалась.Но я это не проверял.

command1 || exit;
command2 || exit;

Bash также сохранит код завершения последней команды в переменной $?.

[ $? -eq 0 ] || exit $?; # exit for none-zero return code

http://cfaj.freeshell.org/shell/cus-faq-2.html#11

  1. Как мне получить код выхода cmd1 в cmd1|cmd2

    Во-первых, обратите внимание, что cmd1 Код выхода может быть ненулевым и при этом не означает ошибку.Это происходит, например, в

    cmd | head -1
    

    вы можете наблюдать статус выхода 141 (или 269 с ksh93) cmd1, но это потому, что cmd было прервано сигналом SIGPIPE, когдаhead -1 завершается после прочтения одной строки.

    Чтобы узнать статус выхода элементов конвейераcmd1 | cmd2 | cmd3

    а.с ЗШ:

    Коды выхода предоставляются в специальном массиве Pipestatus. cmd1 код выхода находится в $pipestatus[1], cmd3 код выхода в $pipestatus[3], так что $? всегда то же самое, что и $pipestatus[-1].

    б.с Башем:

    Коды выхода указаны в PIPESTATUS специальный массив. cmd1 код выхода находится в ${PIPESTATUS[0]}, cmd3 код выхода в ${PIPESTATUS[2]}, так что $? всегда то же самое, что и ${PIPESTATUS: -1}.

    ...

    Для получения более подробной информации см. следующее связь.

для Баша:

# this will trap any errors or commands with non-zero exit status
# by calling function catch_errors()
trap catch_errors ERR;

#
# ... the rest of the script goes here
#  

function catch_errors() {
   # do whatever on errors
   # 
   #
   echo "script aborted, because of errors";
   exit 0;
}

В bash это легко, просто свяжите их вместе с помощью &&:

command1 && command2 && command3

Вы также можете использовать вложенную конструкцию if:

if command1
   then
       if command2
           then
               do_something
           else
               exit
       fi
   else
       exit
fi
#
#------------------------------------------------------------------------------
# run a command on failure exit with message
# doPrintHelp: doRunCmdOrExit "$cmd"
# call by:
# set -e ; doRunCmdOrExit "$cmd" ; set +e
#------------------------------------------------------------------------------
doRunCmdOrExit(){
    cmd="$@" ;

    doLog "DEBUG running cmd or exit: \"$cmd\""
    msg=$($cmd 2>&1)
    export exit_code=$?

    # if occured during the execution exit with error
    error_msg="Failed to run the command:
        \"$cmd\" with the output:
        \"$msg\" !!!"

    if [ $exit_code -ne 0 ] ; then
        doLog "ERROR $msg"
        doLog "FATAL $msg"
        doExit "$exit_code" "$error_msg"
    else
        #if no errors occured just log the message
        doLog "DEBUG : cmdoutput : \"$msg\""
        doLog "INFO  $msg"
    fi

}
#eof func doRunCmdOrExit
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top