Сценарий выхода из оболочки на основе кода выхода процесса
Вопрос
У меня есть сценарий оболочки, который выполняет ряд команд.Как завершить работу сценария оболочки, если какая-либо из команд завершается с ненулевым кодом выхода?
Решение
После каждой команды код выхода можно найти в $?
переменная, чтобы у вас было что-то вроде:
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
Как мне получить код выхода
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