Jest to udokumentowane (dla POSIX) w rozdziale 2.9.1 Proste polecenia
specyfikacji bazowej grupy otwartej. Jest tam ściana tekstu; Zwracam uwagę na ostatni akapit:
Jeśli istnieje nazwa polecenia, wykonywanie będzie kontynuowane zgodnie z opisem w rozdziale Wyszukiwanie i wykonywanie poleceń . Jeżeli nie ma nazwy polecenia, ale polecenie zawierało podstawienie polecenia, polecenie powinno uzupełnić się o status wyjścia ostatniego wykonanego zastąpienia polecenia. W przeciwnym razie polecenie powinno zakończyć się z zerowym statusem wyjścia.
Na przykład
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"
Tak też działa bash. Ale patrz także sekcja „nie taka prosta” na końcu.
phk , w swoim pytaniu Przydziały są jak polecenia ze statusem wyjścia, z wyjątkiem przypadków zastąpienia poleceń? , wskazuje
… Wygląda na to, że samo przypisanie liczy się jako polecenie… z zerową wartością wyjściową, ale ma zastosowanie przed prawą stroną przypisania (np. Wywołanie zamiany polecenia…)
To nie jest okropny sposób patrzenia na to. Surowy schemat określania stanu powrotu prostego polecenia (nie zawierającą ;
, &
, |
, &&
i ||
) stanowi:
- Skanuj linię od lewej do prawej, aż dojdziesz do końca lub słowa polecenia (zwykle nazwy programu).
- Jeśli zobaczysz przypisanie zmiennej, zwracanym stanem linii może być po prostu 0.
- Jeśli zobaczysz podstawienie polecenia - tj.
$(…)
- weź status wyjścia z tego polecenia.
- Jeśli osiągniesz rzeczywiste polecenie (nie zastępując polecenia), weź status wyjścia z tego polecenia.
Status zwracany dla linii to ostatni napotkany numer.
Podstawienia poleceń jako argumenty polecenia, np. foo $(bar)
Nie liczą się; otrzymujesz status wyjścia z foo
. Parafrazując notację phk , zachowanie jest tutaj
temporary_variable = EXECUTE( "bar" )
overall_exit_status = EXECUTE( "foo", temporary_variable )
Jest to jednak niewielkie uproszczenie. Ogólny status zwrotu z
A = $ ( cmd 1 ) B = $ ( cmd 2 ) C = $ ( cmd 3 ) D = $ ( cmd 4 ) E = mc 2
to status wyjścia z . Zadanie, które występuje po przydział nie określa ogólny stan wyjścia na 0.
cmd4
E=
D=
Ikarus w swojej odpowiedzi na pytanie phk podnosi ważną kwestię: zmienne mogą być ustawione jako tylko do odczytu. Akapit od trzeciego do ostatniego w sekcji 2.9.1 standardu POSIX mówi:
Jeśli którekolwiek z przypisań zmiennych próbuje przypisać wartość do zmiennej, dla której ustawiono atrybut tylko do odczytu w bieżącym środowisku powłoki (niezależnie od tego, czy przypisanie jest wykonane w tym środowisku), wystąpi błąd przypisania zmiennej. Zobacz Konsekwencje błędów powłoki, aby poznać konsekwencje tych błędów.
więc jeśli powiesz
readonly A
C=Garfield A=Felix T=Tigger
zwracany jest 1. Nie ma znaczenia, czy struny Garfield
, Felix
i / lub Tigger
są zastępowane podstawiania poleceń (S) - ale patrz uwagi poniżej.
Sekcja 2.8.1 Konsekwencje błędów powłoki zawiera kolejną wiązkę tekstu oraz tabelę i kończy się na
We wszystkich przypadkach pokazanych w tabeli, w których wymagana jest powłoka interaktywna, aby nie wychodzić, powłoka nie wykonuje dalszego przetwarzania polecenia, w którym wystąpił błąd.
Niektóre szczegóły mają sens; niektórzy nie:
A=
Przypisanie czasami przerywa linię poleceń, jako że ostatnie zdanie wydaje się określić. W powyższym przykładzie C
jest ustawione na Garfield
, ale T
nie jest ustawione (i oczywiście nie jest A
).
- Podobnie
wykonuje się,
ale nie . Ale w moich wersjach bash (które obejmują 4.1.x oraz 4.3.x), to ma wykonać . (Nawiasem mówiąc, utrudnia to interpretację phk, że wartość wyjściowa przypisania obowiązuje przed prawą stroną przypisania).
C=$(cmd1) A=$(cmd2) T=$(cmd3)
cmd1
cmd3
cmd2
Ale oto niespodzianka:
W moich wersjach bash
tylko do odczytu A
C = coś A = coś T = coś cmd 0
ma wykonać. W szczególności,cmd0
C = $ ( cmd 1 ) A = $ ( cmd 2 ) T = $ ( cmd 3 ) cmd 0
wykonuje
i , ale nie . (Zauważ, że jest to przeciwieństwo jego zachowania, gdy nie ma polecenia.) I ustawia (jak również ) w środowisku . Zastanawiam się, czy to błąd w bashu.
cmd1
cmd3
cmd2
T
C
cmd0
Nie takie proste:
Pierwszy akapit tej odpowiedzi odnosi się do „prostych poleceń”.
Specyfikacja mówi:
„Proste polecenie” jest sekwencją opcjonalnych przypisań zmiennych i przekierowań, w dowolnej sekwencji, opcjonalnie po słowach i przekierowaniach, zakończonych operatorem sterującym.
Są to instrukcje podobne do tych w moim pierwszym bloku przykładowym:
$ FOO=BAR
$ FOO=$(bar)
$ FOO=$(bar) baz
$ foo $(bar)
trzy pierwsze z nich zawierają przypisania zmiennych, a trzy ostatnie z nich zastępują polecenia.
Ale niektóre przypisania zmiennych nie są tak proste.
bash (1) mówi:
Instrukcje przypisania mogą również pojawić się jako argumenty do alias
, declare
, typeset
, export
, readonly
, i local
wbudowane polecenia ( deklaracji poleceń).
Dla export
, specyfikacja POSIX mówi,
STATUS WYJŚCIA
0Wszystkie operandy nazw zostały pomyślnie wyeksportowane.
> 0Nie można wyeksportować co najmniej jednej nazwy lub -p
podano opcję i wystąpił błąd.
I POSIX nie obsługuje local
, ale bash (1) mówi:
Jest to błąd do użycia, local
gdy nie znajduje się w funkcji. Zwracany status to 0, chyba że local
zostanie użyty poza funkcją, podana zostanie niepoprawna nazwa lub nazwa jest zmienną tylko do odczytu.
Czytając między wierszami, widzimy, że polecenia deklaracji jak
export FOO=$(bar)
i
local FOO=$(bar)
są bardziej jak
foo $(bar)
o ile ignorować stan wyjścia z bar
i daje status wyjścia opartą na głównej komendy ( export
, local
lub foo
). Mamy więc dziwactwo
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”)
co możemy zademonstrować
$ 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
i
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
Na szczęście ShellCheck wyłapuje błąd i podnosi SC2155 , który to zaleca
export foo="$(mycmd)"
należy zmienić na
foo=$(mycmd)
export foo
i
local foo="$(mycmd)"
należy zmienić na
local foo
foo=$(mycmd)
local
się z tym wiąże? Na przykładlocal foo=$(bar)
?