Чи є спосіб, щоб функція в моєму скрипті bash auto запускалася на будь-якій помилці команди?


12

Я пишу сценарій оболонки, який повинен виконати купу команд, і кожна команда залежить від кожної попередньої команди. Якщо будь-яка команда не працює, весь сценарій повинен вийти з ладу, і я викликаю функцію виходу. Я міг би перевірити код виходу кожної команди, але мені цікаво, чи є режим, який я можу включити, або спосіб отримати баш, щоб зробити це автоматично.

Наприклад, візьміть ці команди:

cd foo || myfunc
rm a || myfunc
cd bar || myfunc
rm b || myfunc


Чи є спосіб, коли я можу якимось чином подати сигнал оболонці перед виконанням цих команд, що він повинен викликати myfunc, якщо будь-яка з них виходить з ладу, так що замість цього я можу написати щось більш чисте, наприклад:

cd foo
rm a
cd bar
rm b

Відповіді:


13

Ви можете використовувати ERR bash trap, щоб змусити ваш скрипт вийти, якщо будь-яка команда повертає статус, більший за нуль, і виконувати свою функцію при виході.

Щось на зразок:

myfunc() {
  echo 'Error raised. Exiting!'
}

trap 'myfunc' ERR

# file does not exist, causing error
ls asd
echo 123

Зауважте, що bash trap ERR робить неявним set -o errexitабо set -eне є POSIX.

І ERRпастка не виконується , якщо відмовив команда є частиною списку команд відразу після untilабо whileключового слова, частина тесту , слідуючи ifабо elifзарезервовані слова, частина команди виконується в &&або ||списку, або якщо статус повернення командного є інвертуються з допомогою !.


1

(Можливо) більш простий варіант прийнятої відповіді:

  1. Використовуйте, set -e щоб викликати збій однієї команди для припинення виконання списку.
  2. Просто перерахуйте свої команди.
  3. Використовуйте оператор if- then- elseдля виконання ваших команд (ів) з обробки помилок. Цей останній твір трохи хитромудрий. Дивитися:
встановити -е
якщо
    cmd 1                         # наприклад, cd foo
     cmd 2                         # напр., rm a
     cmd 3                         # наприклад, cd bar
     cmd 4                         # наприклад, rm b
тоді
    встановити + е
    команди для успіху (якщо такі є)
ще
    встановити + е
    myfunc
    інші команди, які потрібно виконати при відмові (якщо такі є) 
fi

Хитрість в тому , що ви кладете ваші команди в в ifчастині від if- then- else, а НЕ thenчастина або elseчастини. Нагадаємо, що синтаксис ifвисловлювання є

якщо  список ; потім  список ; [  список elif ; потім  список ; ] ... [інший  список ; ] fi 
   ↑↑↑↑
set -eКаже оболонці , що, якщо ( ) зазнає невдачі, він не повинен йти далі і виконати ( ), і так далі вниз по лінії. Якби це сталося з командою на самому зовнішньому рівні скрипту оболонки, оболонка вийшла б. Однак, оскільки · · · є (складеним) списком, що слідує за , невдача будь-якої з цих чотирьох команд просто призводить до відмови всього списку - що спричиняє виконання пункту. Якщо всі чотири команди вдаються, пункт виконується.cmd1cd foocmd2rm acmd1cmd2cmd3cmd4ifelsethen

У будь-якому випадку, перше, що вам слід зробити - це, ймовірно, відключити (вимкнути) eваріант, виконуючи це set +e. В іншому випадку скрипт може вибухнути з води, якщо команда myfuncне виконана.

set -eяк зазначено і описано в специфікації POSIX .


0

Взяте слово " кожна команда залежить від кожної попередньої команди. Якщо будь-яка команда не вдається, весь сценарій повинен вийти з ладу " буквально, я думаю, вам не потрібна спеціальна функція для лікування помилок.

Все, що вам потрібно - це зв’язати свої команди з &&оператором і ||оператором, який виконує саме те, що ви написали.

Наприклад, цей ланцюг розірветься і надрукує "щось пішло не так", якщо будь-яка з попередніх команд зламалася (bash читається зліва направо)

cd foo && rm a && cd bar && rm b || echo "something went wrong"

Реальний приклад (я створив dir foo, файл a, dir bar та файл b просто для справжньої демонстрації):

gv@debian:/home/gv/Desktop/PythonTests$ cd foo && rm a && cd bar && rm bb || echo "something is wrong"
rm: cannot remove 'bb': No such file or directory
something is wrong #mind the error in the last command

gv@debian:/home/gv/Desktop/PythonTests$ cd foo && rm aa && cd bar && rm b || echo "something is wrong"
rm: cannot remove 'aa': No such file or directory
something is wrong #mind the error in second command in the row

І нарешті, якщо всі команди були успішно виконані (код виходу 0), сценарій просто продовжується:

gv@debian:/home/gv/Desktop/PythonTests$ cd foo && rm a && cd bar && rm b || echo "something is wrong"
gv@debian:/home/gv/Desktop/PythonTests/foo/bar$ 
# mind that the error message is not printed since all commands were successful.

Важливо пам’ятати, що при використанні && наступна команда виконується, якщо попередня команда вийшла з кодом 0, що для bash означає успіх.

Якщо будь-яка команда піде не так у ланцюзі, тоді команда / скрипт / все, що далі || буде виконано.

І лише для запису: Якщо вам потрібно виконати різні дії залежно від команди, яка зламалася, ви також можете зробити це за допомогою класичного сценарію, відстежуючи значення, $?яке повідомляє про вихідний код саме попередньої команди (повертає нуль, якщо команда виконана успішно або інше додатне число, якщо команда не вдалася)

Приклад:

for comm in {"cd foo","rm a","cd bbar","rm b"};do  #mind the error in third command
eval $comm
    if [[ $? -ne 0 ]];then 
        echo "something is wrong in command $comm"
        break
    else 
    echo "command $comm executed succesful"
    fi
done

Вихід:

command cd foo executed succesfull
command rm a executed succesfull
bash: cd: bbar: No such file or directory
something is wrong in command cd bbar

Порада: Ви можете придушити повідомлення "bash: cd: bbar: Немає такого файлу ...", застосувавши eval $comm 2>/dev/null

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.