скрипт выхода из подоболочки


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
я хочу, чтобы функция "stop" выводила сообщение в stderr и останавливала скрипт, но не останавливается, когда функция, которая вызывает stop, выполняется в под-оболочке, как в примере
Ernest AC

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

1
я не могу вызвать его напрямую, потому что он возвращает строку, которая должна храниться в переменной
Ernest AC

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

1
@htor Я изменил пример
Эрнест А.С.

Ответы:


10

Вы могли бы убить оригинальный shell ( kill $$) перед вызовом exit, и это, вероятно, сработает. Но:

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

Вместо этого вы можете использовать один из нескольких способов вернуть значение в Bash FAQ . К сожалению, большинство из них не так уж и хороши. Вы можете просто застрять, проверяя ошибки после каждого вызова функции ( -eимеет много проблем ). Либо так, либо переключитесь на Perl.


5
Спасибо. Я бы предпочел перейти на Python.
Эрнест А.С.

2
Как я пишу, это 2019 год. Говорить кому-то "переключиться на Perl" - это смешно. Извините, что спорим, но не могли бы вы сказать кому-нибудь, разочарованному «С», перейти на Кобол, что эквивалентно ИМО? Как отмечает Эрнест, Python - гораздо лучший выбор. Я бы предпочел Ruby. В любом случае, ничего кроме 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

Interresting! Есть ли какая-то удача в (довольно старом) bash, у которого нет -E? может быть, мы должны прибегнуть к определению ловушки для сигнала USER и использованию уничтожения для этого сигнала? Я тоже проведу некоторые исследования ...
Оливье Дюлак,

Как узнать, когда не в ловушке суб-оболочки, чтобы вернуть 1 вместо 77?
выступление

7

В качестве альтернативы kill $$, вы также можете попробовать kill 0, это будет работать в случае вложенных подоболочек (все вызывающие и побочные процессы будут получать сигнал) ... но это все еще жестоко и безобразно.


2
Разве это не убило бы идентификатор процесса 0?
Эрнест А.С.

5
Это убьет всю группу процессов. Вы можете ударить вещи, которые вам не нужны (например, если вы начали некоторые вещи в фоновом режиме).
Дероберт

2
@ErnestAC см. Справочную страницу kill (2), pids ≤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 (см. 2 прогона)
  • Когда ifтест пройден false, мы никогда не достигнем подоболочки.

Это очень похоже на оригинальную идею, о которой пользователь писал и сказал, что она не работает. Я не думаю, что это работает для случая подоболочки. Ваш тест, использующий false, завершается после случая "shell" и никогда не попадает в тестовый "subshell". Я полагаю, что в этом случае произойдет сбой, так как подоболочка выйдет из вызова «exit 1», но не распространит ошибку на внешнюю оболочку.
stuckj

0

(Конкретный ответ Bash) Bash не имеет понятия об исключениях. Однако, если установить -o errexit (или эквивалентно: set -e) на крайнем внешнем уровне, команда с ошибкой приведет к тому, что подоболочка завершится с ненулевым состоянием выхода. Если это набор вложенных субоболочек без условий вокруг выполнения этих субоболочек, он будет эффективно «свернуть» весь сценарий и завершится.

Это может быть сложно, когда вы пытаетесь включить биты различного кода bash в больший скрипт. Один кусок 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] # cat / tmp / foo
#! / Bin / Баш
стоп () {
    echo "$ {1}"
    выход 1
}

если ложно; тогда
    эхо "фу"
еще
    (
        остановить "что-то пошло не так"
    )
    эхо "Но мы все равно попали сюда"
фи
[192.168.13.16 (f0f5e19e) ~ 22:58:40] #

-2

Мой пример выхода в один лайнер:

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

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