it-swarm.com.ru

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

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

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

532
samoz

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

exit 1

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

583
Michael Foukarakis

Используйте 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

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

607
Shizzmo

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

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

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

Объяснение

(на основе летающие овцы комментарий).

  • yell: вывести имя сценария и все аргументы в stderr:
    • $0 - путь к скрипту;
    • $* - это все аргументы. 
    • >&2 означает > ПЕРЕНАПРАВИТЬ СТАНДАРТНЫЙ ВЫВОД В & PIPE 2. pipe 1 будет сам stdout
  • die выполняет те же действия, что и yell, но завершается со статусом выхода non-0, что означает «сбой». 
  • try использует || (логическое OR), который оценивает правую сторону только в том случае, если левая сторона не отказала.

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

174
c.gutierrez

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

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

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

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

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

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

21
kavadias

Я часто включаю функцию 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"
10
Joseph Sheedy

Вместо конструкции 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]

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

5
skalee

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

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

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

#!/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

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

0
Gyro Gearloose