Як я можу отримати значення виводу та виходу підпакету під час використання “bash -e”?


70

Розглянемо наступний код

зовнішній-скоп.ш

#!/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()не вдається. Оскільки $()виклик підрозділу, цього не відбувається.

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

Відповіді:


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файлі з однаковими результатами.


Чому це, хоч $? містить статус виходу $ (), що скрипт не виходить автоматично (враховуючи, що встановлено -e)? EDIT: ніколи, я думаю, ви відповіли на мої запитання, дякую!
Джабалсад

Я не впевнений. (Я не перевіряв нічого з перерахованого вище.) Але є деякі обмеження на -e, всі пояснені в мапі сторінки Bash; також, якщо ви запитуєте про це echo $(), то це може бути через те, що коди виходу підпакетів ігноруються, коли рядок - echoкоманда - має свій вихідний код (як правило, 0).
grawity

1
Хм, коли я друкую if ! $(exit 1) ; then echo $?; fi, я отримую 0. Не впевнений, чи ifє шлях, якщо вам потрібно зберегти це значення виходу.
Рон Берк

@grawity - Це працює з Dash? Я намагаюся подолати деяку набридливу поведінку Autoconf (а саме: Autoconf повідомляє про успіх, коли компілятор Sun або IBM друкує незаконний варіант на термінал).
jww

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


3
Хоча це насправді не відповіло на питання, це сьогодні мені було корисно, тому +1.
чотирипаночі

1
Статус виходу команд bash - це завжди остання виконана команда. Коли ми проводимо стільки часу в сильно набраних мовах, легко забути, що "local" - це не специфікатор типу, а лише інша команда. Дякую, що тут повторили цю точку, допомогли мені сьогодні.
markeissler

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 продовжує існувати у другому варіанті ...
Даніель Бек
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.