Як вийти зі скрипту оболонки, якщо одна його частина не працює?


51

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

n=0
until [ $n -ge 5 ]
do
  gksu *command* && break
  n=$[$n+1]
  sleep 3

Відповіді:


88

Одним із підходів було б додати set -eдо початку вашого сценарію. Це означає (від help set):

  -e  Exit immediately if a command exits with a non-zero status.

Тож якщо будь-яка з ваших команд не вдасться, скрипт вийде.

Крім того, ви можете додати явні exitзаяви у можливі точки відмови:

command || exit 1

5
Я (і баш-вікі ) вважаю за краще, щоб люди задумалися над правильним поводженням з помилками, а не з використанням функції (порушена дизайнерським IMO) set -e. Це насправді тут не застосовується. ОП хоче вийти зі сценарію після 5 невдалих спроб запуску команди.
Стефан Шазелас

1
@ StéphaneChazelas Я не буду сперечатися з вами про те, чи зламався він, я впевнений, що ви маєте рацію. Однак ОП запитав "Як я можу написати скрипт оболонки, який закривається, якщо одна його частина виходить з ладу?"
тердон

тому що я не можу придумати іншого способу, як це питання можна інтерпретувати.
Стефан Шазелас

1
@ StéphaneChazelas ти можеш мати рацію. Я тлумачив це буквально: як може вийти весь сценарій, якщо будь-яка його частина виходить з ладу. І set -eце єдиний спосіб, коли я це знаю.
тердон

У цьому сценарій snipet, команди , які можуть викликати set -eце sleep( breakбудучи спеціальної вбудованої команди викличе скрипт для виходу на невдачу в більшості оболонок, команди в ifабо зліва &&не впливає set -e, n=...може потерпіти невдачу , якщо nце тільки для читання, але потім що вийде зі скрипту і без цього set -e), тому інтерпретація звучить зовсім неправдоподібно. Я згоден, питання недостатньо сформульоване.
Стефан Шазелас

17

Ви можете вийти зі сценарію в будь-якому місці за допомогою ключового слова exit. Ви також можете вказати код виходу, щоб вказати іншим програмам, що або як ваш скрипт не вдався, наприклад, exit 1і exit 2т.д. коди вище 127 зарезервовані для ненормального припинення (наприклад, за сигналом)).

Родова конструкція для виходу з ладу є

if [ failure condition ]; then
    exit n
fi

з підходящим failure conditionі n. Але в конкретних сценаріях ви можете діяти інакше. Тепер для вашого випадку я інтерпретую ваше запитання, що якщо будь-яка з п'яти викликів gksuне вдалася, то ви маєте намір вийти. Один із способів - використовувати таку функцію

function try_command {
    for i in 1 2 3 4 5 ; do
        if gksu command ; then
            return 0
        fi
    fi
    exit 1
}

а потім викликати цикл на try_command.

Існують (більш) вдосконалені або складні способи вирішення вашого питання. Однак рішення вище є більш доступним для початківців, ніж, скажімо, рішення Стефана.


10
attempt=0
until gksu command; do
  attempt=$((attempt + 1))
  if [ "$attempt" -gt 5 ]; then
    exit 1
  fi
done

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

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

Наприклад, тут з 2 вкладеними рівнями підшарів:

(
  life=hard
  output=$(
    echo blah
    [ "$life" = easy ] || exit 1 # exit subshell
    echo blih not run
  ) || exit # if the subshell exits with a non-zero exit status,
            # exit as well with the same exit status

  echo not run either
) || exit # if the subshell exits with a non-zero exit status,
          # exit as well with the same exit status

Це може стати складнішим, якщо нижній корпус є частиною трубопроводу. bashмає спеціальний $PIPESTATUSмасив, схожий на zsh«и $pipestatusтой , який може допомогти вам тут:

{
   echo foo
   exit 1
   echo bar
} | wc -c
subshell_ret=${PIPESTATUS[0]}
if [ "$subshell_ret" -ne 0 ]; then
  exit "$subshell_ret"
fi

3

Пастка виконає дію після отримання сигналу.

trap "echo EXIT;  exit" 0
trap "echo HUP;   exit" 1
trap "echo CTL-C; exit" 2
trap "echo QUIT;  exit" 3
trap "echo ERR;   exit" ERR
n=0
until [ $n -ge 5 ]
do
  n=$[$n+1]
  echo $n
  sleep 3
done

Запустіть це і дайте йому нормально вийти. Він вловлює сигнал 0.

EXIT

Запустіть його ще раз і перервіть з ^ C. Він захоплює і сигнал 2, і сигнал 0.

CTL-C
EXIT

Помилка ERR не потрапляє на нуль

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