Как получить выходные и выходные значения подоболочки при использовании «bash -e»?

70

Рассмотрим следующий код

outer-scope.sh

#!/bin/bash
set -e
source inner-scope.sh
echo $(inner)
echo "I thought I would've died :("

inner-scope.sh

#!/bin/bash
function inner() { echo "winner"; return 1; }

Я пытаюсь outer-scope.shвыйти, когда inner()не удается позвонить . Так как $()вызывает вложенную оболочку, этого не происходит.

Как еще можно получить выходные данные функции, сохранив тот факт, что функция может завершиться с ненулевым кодом выхода?

jabalsad
источник

Ответы:

102

$()сохраняет статус выхода; Вы просто должны использовать его в утверждении, которое не имеет собственного статуса, например, в присваивании.

Выход = $ (внутренний)

После этого $?будет содержать состояние выхода inner, и вы можете использовать для этого все виды проверок:

output=$(inner) || exit $?
echo $output

Или же:

if ! output=$(inner); then
    exit $?
fi
echo $output

Или же:

if output=$(inner); then
    echo $output
else
    exit $?
fi

(Примечание. Голое exitбез аргументов эквивалентно exit $?- то есть выходит из состояния выхода последней команды. Вторую форму я использовал только для ясности.)


Кроме того, для записи: sourceсовершенно не связано в этом случае. Вы можете просто определить inner()в outer-scope.shфайле, с теми же результатами.

grawity
источник
Почему это так, хотя $? содержит состояние выхода $ (), которое скрипт не завершает автоматически (при условии, что -e установлен)? РЕДАКТИРОВАТЬ: не важно, я думаю, что вы ответили на мои вопросы, спасибо!
Джабалсад
Я не уверен. (Я не проверял ничего из вышеперечисленного.) Но есть некоторые ограничения для -e, все объяснено на man-странице bash; Кроме того, если вы спрашиваете об этом echo $(), то это может быть из-за того, что коды выхода подоболочек игнорируются, когда строка - echoкоманда - имеет собственный код выхода (обычно 0).
grawity
1
Хм, когда я печатаю if ! $(exit 1) ; then echo $?; fi, я получаю 0. Не уверен, что ifэто правильный путь, если вам нужно сохранить это значение выхода.
Рон Берк
@ Grawity - это работает с Dash? Я пытаюсь обойти какое-то раздражающее поведение Autoconf (а именно, Autoconf сообщает об успехе, когда компилятор Sun или IBM выводит на терминал недопустимую опцию ).
17
3
if ! output=$(inner); then exit $?; fiвыйдет с кодом возврата 0, потому $?что !вместо кода возврата будет указан код возврата inner. Вы могли бы получить желаемое поведение с, if output=$(inner); then : ; else exit $?; fiно это, очевидно, более многословно
SJL
26

Смотрите BashFAQ / 002 :

Если вы хотите оба (выходной и выходной статус):

output=$(command)
status=$? 

Особый случай

Обратите внимание на сложный случай с локальными переменными функции, сравните следующий код:

f() { local    v=$(echo data; false); echo output:$v, status:$?; }
g() { local v; v=$(echo data; false); echo output:$v, status:$?; }

Мы получим:

$ f     # fooled by 'local' with inline initialization
output:data, status:0

$ g     # a good one
output:data, status:1

Почему?

Когда выходные данные подоболочки используются для инициализации localпеременной, состояние выхода больше не подчиненной оболочки, а команды , что, скорее всего, будет .local 0

Смотрите также https://stackoverflow.com/a/4421282/537554

ryenus
источник
3
Хотя это на самом деле не ответило на вопрос, сегодня оно мне пригодилось, поэтому +1.
четверг в ночь на
1
Состояние выхода для команд bash всегда соответствует состоянию последней выполненной команды. Когда мы проводим так много времени в строго типизированных языках, легко забыть, что «local» - это не спецификатор типа, а просто другая команда. Спасибо за повторение этой точки здесь, помог мне сегодня.
Маркисслер
2
Ух ты, я только сейчас столкнулся с этой проблемой, а ты прояснил ее. Спасибо!
krb686
4
#!/bin/bash
set -e
source inner-scope.sh
foo=$(inner)
echo $foo
echo "I thought I would've died :("

Добавляя echo, подоболочка не стоит отдельно (не проверяется отдельно) и не прерывается. Назначение обходит эту проблему.

Вы также можете сделать это и перенаправить вывод в файл для последующей обработки.

tmpfile=$( mktemp )
inner > $tmpfile
cat $tmpfile
rm $tmpfile
Даниэль Бек
источник
Конечно, $ tmpfile продолжает существовать во втором варианте ...
Даниэль Бек