В скрипте Bash, как я могу выйти из всего скрипта, если возникает определенное условие?

Я пишу сценарий на Bash для проверки кода. Тем не менее, кажется глупым запускать тесты, если компиляция кода в первую очередь не удалась, и в этом случае я просто прерву тесты.

Есть ли способ, которым я могу сделать это, не оборачивая весь скрипт внутри цикла while и не используя разрывы? Что-то вроде Дун Дун Дун Гото?

10 ответов

Решение

Попробуйте это утверждение:

exit 1

замещать 1 с соответствующими кодами ошибок. См. Также Коды выхода с особыми значениями.

Используйте set -e

#!/bin/bash

set -e

/bin/command-that-fails
/bin/command-that-fails2

Сценарий завершится после первой неудачной строки (возвращает ненулевой код завершения). В этом случае command-that-fails2 не будет выполняться.

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

#!/bin/bash

# I'm assuming you're using make

cd /project-dir
make
if [[ $? -ne 0 ]] ; then
    exit 1
fi

cd /project-dir2
make
if [[ $? -ne 0 ]] ; then
    exit 1
fi

С set -e это будет выглядеть так:

#!/bin/bash

set -e

cd /project-dir
make

cd /project-dir2
make

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

Плохой парень из SysOps однажды научил меня технике Трехпалого Когтя:

yell() { echo "$0: $*" >&2; }
die() { yell "$*"; exit 111; }
try() { "$@" || die "cannot $*"; }

Эти функции * ОС NIX и оболочка устойчивы. Поместите их в начало вашего сценария (bash или иным образом), try() Ваше заявление и код на.

объяснение

(основываясь на комментариях flying sheep).

  • yell: вывести имя скрипта и все аргументы stderr:
    • $0 путь к сценарию;
    • $* все аргументы.
    • >&2 средства >перенаправить стандартный вывод на & pipe2, труба1 было бы stdout сам.
  • die делает так же, как yell, но выходит со статусом выхода не равным 0, что означает "сбой".
  • try использует || (логическое OR), который оценивает правую сторону только в том случае, если левая не провалилась.
    • $@ Это все аргументы снова, но разные.

Надеюсь, это все объясняет.

Если вы будете вызывать скрипт с source, ты можешь использовать return <x> где <x> будет состояние выхода скрипта (используйте ненулевое значение для ошибки или false). Это также будет работать, как и ожидалось, когда вы source сценария, но если вы вызываете исполняемый сценарий (т. е. непосредственно с его именем файла), оператор return приведет к жалобе (сообщение об ошибке "return: может только" вернуться "из функции или сценария с источником").

Если exit <x> вместо этого используется, когда скрипт вызывается с source, это приведет к выходу из оболочки, которая запустила скрипт, но исполняемый скрипт будет работать нормально.

Чтобы обработать любой случай в одном и том же сценарии, вы можете использовать

return <x> 2> /dev/null || exit <x>

Это будет обрабатывать любой вызов может быть подходящим.

Замечания: <x> должен быть просто числом.

Я часто включаю функцию run() для обработки ошибок. Каждый вызов, который я хочу сделать, передается этой функции, поэтому весь сценарий завершается, когда происходит сбой. Преимущество этого по сравнению с решением set -e заключается в том, что сценарий не завершает работу без вывода сообщений при сбое строки и может сказать вам, в чем проблема. В следующем примере 3-я строка не выполняется, потому что сценарий завершается при вызове false.

function run() {
  cmd_output=$(eval $1)
  return_value=$?
  if [ $return_value != 0 ]; then
    echo "Command $1 failed"
    exit -1
  else
    echo "output: $cmd_output"
    echo "Command succeeded."
  fi
  return $return_value
}
run "date"
run "false"
run "date"

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

#!/usr/bin/env bash

echo $[1+1]
echo $[2/0]              # division by 0 but execution of script proceeds
echo $[3+1]
(echo $[4/0]) || exit $? # script halted with code 1 returned from `echo`
echo $[5+1]

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

      #!/bin/bash -x

# exit and report the failure if any command fails
exit_trap () {                                         # ---- (1)
  local lc="$BASH_COMMAND" rc=$?
  echo "Command [$lc] exited with code [$rc]"
}

trap exit_trap EXIT                                    # ---- (2)

set -e                                                 # ---- (3)

Объяснение:

Этот вопрос также касается того, как писать чистый код. Давайте разделим приведенный выше скрипт на несколько частей:


Часть - 1: это функция, которая вызывается при сбое любого шага и фиксирует последний выполненный шаг, используяи фиксирует код возврата этого шага. Это функция, которую можно использовать для любой очистки, как и в случае с shutdownhooks .

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

Док.


Часть 2:

      trap [action] [signal]

Зарегистрируйте действие ловушки (здесьфункция) в случае сигнала EXIT .


Часть - 3:

Немедленно выйти, если последовательность из одной или нескольких команд возвращает ненулевой статус. Оболочка не завершается, если команда, которая не удалась, является частью списка команд, следующей за ключевым словом while или until, частью теста в операторе if, частью любой команды, выполняемой в операторах && или || список, за исключением команды, следующей за конечным символом && или ||, любой команды в конвейере, кроме последней, или если статус возврата команды инвертируется с помощью !. Если составная команда, отличная от подоболочки, возвращает ненулевое состояние из-за сбоя команды, в то время как параметр -e игнорировался, оболочка не завершается. Ловушка ERR, если она установлена, выполняется перед выходом из оболочки.

Док.


Часть - 4:

Вы можете создатьфайл и источник его во всех ваших сценариях.

      source common.sh

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

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

Вот небольшой скрипт о том, как это сделать:

#!/bin/bash

boom() {
    while true; do sleep 1.2; echo boom; done
}

f() {
    echo Hello
    N=0
    while
        ((N++ <10))
    do
        sleep 1
        echo $N
        #        ((N > 5)) && exit 4 # does not work
        ((N > 5)) && { kill -9 $$; exit 5; } # works 
    done
}

boom &
f &

while true; do sleep 0.5; echo beep; done

Это лучший ответ, но все еще неполный, и я действительно не знаю, как избавиться от части бума.

Вы можете закрыть свою программу по имени программы следующим образом:

для мягкого выхода делать

      pkill -9 -x programname # Replace "programmname" by your programme

для жесткого выхода делать

      pkill -15 -x programname # Replace "programmname" by your programme

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

Вы можете закрыть свою программу по PID программы следующим образом:

для мягкого выхода делать

      kill -9 $PID # Replace "programmname" by your programme

для жесткого выхода делать

      kill -15 $PID # Replace "programmname" by your programme

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

Другие вопросы по тегам