Скрипт выхода из оболочки на основе кода выхода процесса
378
У меня есть сценарий оболочки, который выполняет ряд команд. Как заставить скрипт сценария завершиться, если любая из команд завершается с ненулевым кодом выхода?
Я ответил, что вы используете bash, но если это совсем другая оболочка, можете ли вы указать это в своем посте?
Мартин У
Жесткий метод: проверять значение $?после каждой команды. Простой способ: поставить set -eили #!/bin/bash -eв верхней части вашего сценария Bash.
mwfearnley
Ответы:
495
После каждой команды код выхода можно найти в $?переменной, так что вы получите что-то вроде:
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команды, а не всего конвейера. Вы также можете получить весь список для обработки по своему усмотрению:
Та же особенность только в одной строке переносимого кода: ls -al file.ext || exit $?([[]] не переносима)
2010 г.,
19
MarcH, я думаю, вы обнаружите, что [[ ]]это довольно переносимо bash, и это вопрос, которым помечен вопрос :-) Как ни странно, lsон не работает, command.comтак что он тоже не переносим, я знаю, но это тот же аргумент, что и вы. подарок.
paxdiablo
39
Я знаю, что это древнее, но следует отметить, что вы можете получить код выхода команд в трубе через массив PIPESTATUS(т . ${PIPESTATUS[0]}${PIPESTATUS[1]}${PIPESTATUS[*]}
Е.
11
Следует подчеркнуть, что элегантные и идиоматические сценарии оболочки очень редко требуют $?непосредственного изучения . Вы обычно хотите что-то вроде того, if ls -al file.ext; then : nothing; else exit $?; fiчто, как, например, @MarcH говорит, эквивалентно, ls -al file.ext || exit $?но если предложения thenили elseявляются несколько более сложными, это более удобно для обслуживания.
tripleee
9
[[ $rc != 0 ]]даст вам 0: not foundили 1: not foundошибку. Это должно быть изменено на [ $rc -ne 0 ]. Также rc=$?может быть удален и просто использован [ $? -ne 0 ].
CurtisLeeBolin
223
Если вы хотите работать с $ ?, вам нужно будет проверять его после каждой команды, так как $? обновляется после выхода каждой команды. Это означает, что если вы выполните конвейер, вы получите только код завершения последнего процесса в конвейере.
Другой подход заключается в том, чтобы сделать это:
set-e
set-o pipefail
Если вы поместите это в начало сценария оболочки, похоже, bash позаботится об этом за вас. Как отмечалось в предыдущем постере, «set -e» приведет к выходу bash с ошибкой в любой простой команде. "set -o pipefail" приведет к завершению работы bash с ошибкой и для любой команды в конвейере.
Смотрите здесь или здесь для более подробного обсуждения этой проблемы. Вот раздел руководства bash по встроенному набору.
Это действительно должен быть главный ответ: сделать это намного, намного проще, чем использовать PIPESTATUSи проверять коды выхода везде.
Candu
2
#!/bin/bash -eэто единственный способ запустить скрипт оболочки. Вы всегда можете использовать такие вещи, как, например, foo || handle_error $?если вам нужно проверить статусы выхода.
Дэвис Херринг
53
« set -e», вероятно, самый простой способ сделать это. Просто поместите это перед любыми командами в вашей программе.
@SwaroopCH set -eваш скрипт будет прерван , если какая-либо команда в вашем скрипте завершится со статусом ошибки, и вы не обработали эту ошибку.
Андрей
2
set -eна 100% эквивалентно тому, set -o errexitчто в отличие от первого можно искать. Ищите opengroup + errexit для официальной документации.
марта
30
Если вы просто вызовите команду exit в bash без параметров, она вернет код завершения последней команды. В сочетании с ИЛИ bash должен вызывать выход только в случае неудачи предыдущей команды. Но я не проверял это.
command1 || Выход;
command2 || Выход;
Bash также будет хранить код завершения последней команды в переменной $ ?.
Во-первых, обратите внимание, что 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;}
+1 Это было самое простое решение, которое я искал. Кроме того, вы также можете написать, if (! command)если вы ожидаете ненулевой код ошибки от команды.
Берчи
это для последовательных команд ... что делать, если я хочу запустить эти 3 параллельно и убить всех, если какой-либо из них завершится неудачно?
Василе Сурду
4
##------------------------------------------------------------------------------# 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
$?
после каждой команды. Простой способ: поставитьset -e
или#!/bin/bash -e
в верхней части вашего сценария Bash.Ответы:
После каждой команды код выхода можно найти в
$?
переменной, так что вы получите что-то вроде:Вы должны быть осторожны с конвейерными командами, так как
$?
только вы получите код возврата последнего элемента в конвейере, поэтому в коде:не вернет код ошибки, если файл не существует (поскольку
sed
часть конвейера фактически работает, возвращая 0).bash
Оболочка фактически обеспечивает массив , который может помочь в этом случае, что бытьPIPESTATUS
. Этот массив имеет один элемент для каждого из компонентов конвейера, к которому вы можете обращаться по отдельности, например${PIPESTATUS[0]}
:Обратите внимание, что это результат
false
команды, а не всего конвейера. Вы также можете получить весь список для обработки по своему усмотрению:Если вы хотите получить самый большой код ошибки из конвейера, вы можете использовать что-то вроде:
Это проходит каждый из
PIPESTATUS
элементов по очереди, сохраняя его,rc
если он был больше, чем предыдущееrc
значение.источник
ls -al file.ext || exit $?
([[]] не переносима)[[ ]]
это довольно переносимоbash
, и это вопрос, которым помечен вопрос :-) Как ни странно,ls
он не работает,command.com
так что он тоже не переносим, я знаю, но это тот же аргумент, что и вы. подарок.PIPESTATUS
(т .${PIPESTATUS[0]}
${PIPESTATUS[1]}
${PIPESTATUS[*]}
$?
непосредственного изучения . Вы обычно хотите что-то вроде того,if ls -al file.ext; then : nothing; else exit $?; fi
что, как, например, @MarcH говорит, эквивалентно,ls -al file.ext || exit $?
но если предложенияthen
илиelse
являются несколько более сложными, это более удобно для обслуживания.[[ $rc != 0 ]]
даст вам0: not found
или1: not found
ошибку. Это должно быть изменено на[ $rc -ne 0 ]
. Такжеrc=$?
может быть удален и просто использован[ $? -ne 0 ]
.Если вы хотите работать с $ ?, вам нужно будет проверять его после каждой команды, так как $? обновляется после выхода каждой команды. Это означает, что если вы выполните конвейер, вы получите только код завершения последнего процесса в конвейере.
Другой подход заключается в том, чтобы сделать это:
Если вы поместите это в начало сценария оболочки, похоже, bash позаботится об этом за вас. Как отмечалось в предыдущем постере, «set -e» приведет к выходу bash с ошибкой в любой простой команде. "set -o pipefail" приведет к завершению работы bash с ошибкой и для любой команды в конвейере.
Смотрите здесь или здесь для более подробного обсуждения этой проблемы. Вот раздел руководства bash по встроенному набору.
источник
PIPESTATUS
и проверять коды выхода везде.#!/bin/bash -e
это единственный способ запустить скрипт оболочки. Вы всегда можете использовать такие вещи, как, например,foo || handle_error $?
если вам нужно проверить статусы выхода.«
set -e
», вероятно, самый простой способ сделать это. Просто поместите это перед любыми командами в вашей программе.источник
set -e
ваш скрипт будет прерван , если какая-либо команда в вашем скрипте завершится со статусом ошибки, и вы не обработали эту ошибку.set -e
на 100% эквивалентно тому,set -o errexit
что в отличие от первого можно искать. Ищите opengroup + errexit для официальной документации.Если вы просто вызовите команду exit в bash без параметров, она вернет код завершения последней команды. В сочетании с ИЛИ bash должен вызывать выход только в случае неудачи предыдущей команды. Но я не проверял это.
Bash также будет хранить код завершения последней команды в переменной $ ?.
источник
источник
exit $?
(без паренов)?http://cfaj.freeshell.org/shell/cus-faq-2.html#11
Как я могу получить код выхода
cmd1
вcmd1|cmd2
Во-первых, обратите внимание, что
cmd1
код выхода может быть ненулевым и все же не означать ошибку. Это происходит, например, вВы можете наблюдать состояние выхода 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}
....
Для более подробной информации смотрите следующую ссылку .
источник
для Баш:
источник
В bash это легко, просто свяжите их вместе с &&:
Вы также можете использовать вложенную конструкцию if:
источник
if (! command)
если вы ожидаете ненулевой код ошибки от команды.источник
$*
;"$@"
вместо этого используйте для сохранения пробелов и подстановочных знаков.