Це задокументовано (для POSIX) у Розділі 2.9.1 Прості команди
Технічні характеристики бази відкритої групи. Там стіна тексту; Я звертаю вашу увагу на останній абзац:
Якщо є ім'я команди, виконання продовжуватиметься так, як описано в пошуку та виконанні команд . Якщо імені команди немає, але команда містила підстановку команди, команда повинна завершуватись статусом виходу останньої заміни команди, виконаної. В іншому випадку команда повинна завершитися нульовим статусом виходу.
Так, наприклад,
Command Exit Status
$ FOO=BAR 0 (but see also the note from icarus, below)
$ FOO=$(bar) Exit status from "bar"
$ FOO=$(bar) baz Exit status from "baz"
$ foo $(bar) Exit status from "foo"
Так працює і баш. Але дивіться також "не так простий" розділ наприкінці.
phk , в його питанні Призначення - це як команди зі статусом виходу, за винятком випадків, коли є заміна команд? , пропонує
… Здається, що сам присвоєння вважається командою… із нульовим значенням виходу, але яке застосовується перед правою стороною призначення (наприклад, виклик заміни команди…)
Це не страшний спосіб дивитися на це. Неочищена схема для визначення статусу повертається простий команди (яка не містить ;
, &
, |
, &&
або ||
) є:
- Скануйте рядок зліва направо, поки не досягнете кінця або командного слова (зазвичай це ім'я програми).
- Якщо ви бачите змінне призначення, стан повернення для рядка може бути лише 0.
- Якщо ви бачите заміну команди - тобто,
$(…)
- візьміть статус виходу з цієї команди.
- Якщо ви дійшли до фактичної команди (не в підстановці команди), візьміть статус виходу з цієї команди.
Статус повернення для рядка - це останнє число, з яким ви стикалися.
Заміни команд як аргументи для команди, наприклад, foo $(bar)
не враховуються; ви отримуєте статус виходу від foo
. Перефразовуючи позначення phk , поведінка тут така
temporary_variable = EXECUTE( "bar" )
overall_exit_status = EXECUTE( "foo", temporary_variable )
Але це незначне спрощення. Загальний статус повернення від
A = $ ( cmd 1 ) B = $ ( cmd 2 ) C = $ ( cmd 3 ) D = $ ( cmd 4 ) E = mc 2
- статус виходу з . Завдання , яке відбувається після того , як завдання не встановлює статус виходу в цілому 0.
cmd4
E=
D=
Ікар , в своїй відповіді на питання ФГК в , піднімає важливе питання: змінні можуть бути встановлені як незмінні. Абзац третій до останнього в Розділі 2.9.1 стандарту POSIX говорить:
Якщо будь-який із присвоєнь змінної намагається призначити значення змінній, для якої атрибут readonly встановлений у поточному середовищі оболонки (незалежно від того, чи присвоєння виконано в цьому середовищі), виникає помилка присвоювання змінної. Про наслідки цих помилок див. Наслідки помилок оболонки .
тож якщо ти кажеш
readonly A
C=Garfield A=Felix T=Tigger
повертається статус 1. Не має значення , якщо рядки Garfield
, Felix
і / або Tigger
замінені командою заміщення (S) - але бачити примітки нижче.
Розділ 2.8.1 Наслідки помилок оболонки містить ще один текст та таблицю та закінчується на
У всіх випадках, наведених у таблиці, коли інтерактивна оболонка не потребує виходу, оболонка не повинна виконувати подальшу обробку команди, в якій сталася помилка.
Деякі деталі мають сенс; деякі ні:
A=
Завдання іноді перериває командний рядок, як це останнє речення здається уточнити. У наведеному вище прикладі C
встановлено Garfield
, але T
не встановлено (і, звичайно, не є A
).
- Аналогічно
виконує,
але ні . Але в моїх версіях bash (які включають 4.1.X і 4.3.X), він дійсно виконується . (Між іншим, це додатково перешкоджає тлумаченню phk про те, що значення виходу завдання призначається перед правою частиною завдання.)
C=$(cmd1) A=$(cmd2) T=$(cmd3)
cmd1
cmd3
cmd2
Але ось сюрприз:
У моїх версіях bash,
тільки читати A
C = щось A = щось T = щось cmd 0
це виконати. Зокрема,cmd0
C = $ ( cmd 1 ) A = $ ( cmd 2 ) T = $ ( cmd 3 ) cmd 0
виконує
і , але ні . (Зауважте, що це протилежне його поведінці, коли немає команди.) І він встановлює (як і ) в середовищі . Цікаво, чи це помилка в баші.
cmd1
cmd3
cmd2
T
C
cmd0
Не так просто:
Перший абзац цієї відповіді стосується "простих команд".
Специфікація говорить:
"Проста команда" - це послідовність необов'язкових присвоювань змінних та перенаправлень у будь-якій послідовності, необов'язково слідуючи за словами та перенаправленнями, що закінчуються оператором управління.
Це такі твердження, як ті, що в моєму першому прикладі:
$ FOO=BAR
$ FOO=$(bar)
$ FOO=$(bar) baz
$ foo $(bar)
перші три з яких містять змінні призначення, а останні три з яких містять підстановки команд.
Але деякі змінні завдання не такі вже й прості.
bash (1) каже,
Оператори присвоювання можуть також з'являтися в якості аргументів alias
, declare
, typeset
, export
, readonly
, і local
вбудованих команд ( декларації команд).
Для export
, специфікації POSIX каже,
ВИХІД СТАТУС
0Усі операнди імен були успішно експортовані.
> 0Принаймні одне ім’я не вдалося експортувати або -p
було вказано параметр і сталася помилка.
І POSIX не підтримує local
, але bash (1) каже:
Помилка використання, local
коли вона не знаходиться у функції. Стан повернення дорівнює 0, якщо local
не використовується поза функцією, вводиться неправильне ім'я або ім'я є змінною, що читається лише зараз.
Читаючи між рядками, ми можемо побачити такі команди декларації, як
export FOO=$(bar)
і
local FOO=$(bar)
більше схожі
foo $(bar)
оскільки вони ігнорують статус виходу з bar
і дати вам статус виходу на основі головної команди ( export
, local
або foo
). Отже, ми маємо дивацтва
Command Exit Status
$ FOO=$(bar) Exit status from "bar"
(unless FOO is readonly)
$ export FOO=$(bar) 0 (unless FOO is readonly,
or other error from “export”)
$ local FOO=$(bar) 0 (unless FOO is readonly,
statement is not in a function,
or other error from “local”)
що ми можемо продемонструвати
$ export FRIDAY=$(date -d tomorrow)
$ echo "FRIDAY = $FRIDAY, status = $?"
FRIDAY = Fri, May 04, 2018 8:58:30 PM, status = 0
$ export SATURDAY=$(date -d "day after tomorrow")
date: invalid date ‘day after tomorrow’
$ echo "SATURDAY = $SATURDAY, status = $?"
SATURDAY = , status = 0
і
myfunc() {
local x=$(echo "Foo"; true); echo "x = $x -> $?"
local y=$(echo "Bar"; false); echo "y = $y -> $?"
echo -n "BUT! "
local z; z=$(echo "Baz"; false); echo "z = $z -> $?"
}
$ myfunc
x = Foo -> 0
y = Bar -> 0
BUT! z = Baz -> 1
На щастя, ShellCheck виявляє помилку і піднімає SC2155 , що радить це
export foo="$(mycmd)"
слід змінити на
foo=$(mycmd)
export foo
і
local foo="$(mycmd)"
слід змінити на
local foo
foo=$(mycmd)
local
ув'язуєте це? Наприкладlocal foo=$(bar)
?