скрипт вихідної оболонки з підшару


30

Розглянемо цей фрагмент:

stop () {
    echo "${1}" 1>&2
    exit 1
}

func () {
    if false; then
        echo "foo"
    else
        stop "something went wrong"
    fi
}

Зазвичай, коли funcвін викликається, це призведе до припинення дії сценарію, що і є передбачуваною поведінкою. Однак, якщо він виконується в підколонці, наприклад в

result=`func`

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


1
я хочу функцію "стоп", яка друкує повідомлення на stderr і зупиняє скрипт, але вона не зупиняється, коли функція, яка викликає зупинку, виконується в підколонці, як у прикладі
Ernest AC

2
Звичайно, тому що він виходить з нижньої частини корпусу не поточним. Просто викличте функцію безпосередньо: func.

1
я не можу викликати його безпосередньо, тому що він повертає рядок, який повинен бути збережений у змінній
Ernest AC

1
@ErnestAC Будь ласка, вкажіть усі деталі в оригінальному запитанні. Вищенаведена функція не повертає рядок.

1
@htor Я змінив приклад
Ernest AC

Відповіді:


10

Ви можете вбити оригінальну оболонку ( kill $$) перед викликом exit, і це, ймовірно, спрацює. Але:

  • мені здається досить некрасивим
  • він зламається, якщо у вас є друга підзаголовка, тобто використовуйте нижню оболонку всередині.

Натомість ви можете скористатися одним із декількох способів повернути значення в FAQ Bash . На жаль, більшість з них не такі великі. Ви можете просто затримати перевірку на наявність помилок після кожного виклику функції ( -eмає багато проблем ). Або перейдіть на Perl.


5
Спасибі. Я б швидше перейшов на Python.
Ернест AC

2
Як я пишу, це 2019 рік. Сказати комусь «перейти на Perl» - смішно. Вибачте за суперечки, але ви б сказали, хтось засмучений «C» перейти на Cobol, що еквівалент IMO? Як зазначає Ернест, Python - це набагато кращий вибір. Моїм уподобанням буде Рубі. Так чи інакше, окрім Perl.
Грем Ніколлс

38

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

set -E
trap '[ "$?" -ne 77 ] || exit 77' ERR

(
  echo here
  (
    echo there
    (
      exit 12 # not 77, exit only this subshell
    )
    echo ici
    exit 77 # exit all subshells
  )
  echo not here
)
echo not here either

set -Eу поєднанні з ERRпастками трохи нагадує вдосконалену версію set -e, оскільки дозволяє визначити власну операцію з помилками.

У zsh, пастки ERR успадковуються автоматично, тому вам не потрібно set -E, ви також можете визначити пастки як TRAPERR()функції та змінювати їх $functions[TRAPERR], наприклад,functions[TRAPERR]="echo was here; $functions[TRAPERR]"


1
Цікаве рішення! Ясна витонченість, ніж kill $$.

3
Одне, на що слід стежити, ця пастка не обробляє інтерпольовані команди, наприклад echo "$(exit 77)"; сценарій буде продовжуватися так, ніби ми писалиecho ""
Warbo

Інтересуючий! Чи є удача на (досить старший) баш, який не має -E? можливо, нам доведеться вдатися до визначення пастки на сигнал USER і використання вбиття для цього сигналу? Я теж зроблю кілька досліджень ...
Олів'є Дулак

Як дізнатися, якщо не знаходитесь у пастці під оболонки, щоб повернути 1 замість 77?
припинення

7

Як альтернативу kill $$, ви можете також спробувати kill 0, він буде працювати у випадку вкладених підшах (усі абоненти та сторонні процеси отримуватимуть сигнал)… але це все ще жорстоко і некрасиво.


2
Чи не буде це процес вбивства id 0?
Ернест AC

5
Це вб'є всю групу процесів. Ви можете потрапити на речі, які не хочете (якщо, наприклад, ви почали деякі речі у фоновому режимі).
дероберт

2
@ErnestAC див. Сторінку вбивства (2), підкладки ≤0 мають особливе значення.
дероберт

0

Спробуйте це ...

stop () {
    echo "${1}" 1>&2
    exit 1
}

func () {
    if $1; then
        echo "foo"
    else
        stop "something went wrong"
    fi
}

echo "shell..."
func $1

echo "subshell..."
result=`func $1`

echo "shell..."
echo "result=$result"

Отримані результати я ...

# test_exitsubshell true
shell...
foo
subshell...
shell...
result=foo
# test_exitsubshell false
shell...
something went wrong

Примітки

  • Параметризовано для того, щоб ifтест міг бути trueабо false(див. Два запуски)
  • Коли ifтест є false, ми ніколи не доходимо до нижньої частини.

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

0

(Відповідь Баша) Баш не має поняття винятків. Однак, якщо встановити -o errexit (або еквівалент: set -e) на самому зовнішньому рівні, невдала команда призведе до того, що підпакет вийде з ненульовим статусом виходу. Якщо це набір вкладених підшаровок без умовних умов навколо виконання цих підшалл, це ефективно «згортає» весь сценарій і виходить.

Це може бути складним при спробі включити біти різного bash-коду до більшого сценарію. Один шматок баш може працювати просто чудово самостійно, але коли він виконується під errexit (або без errexit), вести себе несподівано.

[192.168.13.16 (f0f5e19e) ~ 22:58:22] # bash -o errexit / tmp / foo
щось пішло не так
[192.168.13.16 (f0f5e19e) ~ 22:58:31] # bash / tmp / foo
щось пішло не так
Але ми все одно потрапили сюди
[192.168.13.16 (f0f5e19e) ~ 22:58:37] # кіт / tmp / foo
#! / бін / баш
Стоп () {
    відлуння "$ {1}"
    вихід 1
}

якщо помилково; потім
    відлуння "foo"
ще
    (
        стоп "щось пішло не так"
    )
    відлуння "Але ми все одно потрапили сюди"
фі
[192.168.13.16 (f0f5e19e) ~ 22:58:40] #

-2

Мій приклад для виходу в одному вкладиші:

COMAND || ( echo "ERROR – executing COMAND, exiting..." ; exit 77 );[ "$?" -eq 77 ] && exit

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