Przydziały są jak polecenia ze statusem wyjścia, z wyjątkiem sytuacji, gdy istnieje zamiana poleceń?


10

Zobacz następujące przykłady i ich wyniki w powłokach POSIX:

  1. false;echo $?lub false || echo 1:1
  2. false;foo="bar";echo $?lub foo="bar" && echo 0:0
  3. foo=$(false);echo $?lub foo=$(false) || echo 1:1
  4. foo=$(true);echo $?lub foo=$(true) && echo 0:0

Jak wspomniano w najczęściej głosowanej odpowiedzi na /programming/6834487/what-is-the-variable-in-shell-scripting :

$? służy do znalezienia wartości zwracanej ostatnio wykonanej komendy.

Jest to prawdopodobnie nieco mylące w tym przypadku, więc pobierzmy definicję POSIX, która jest również cytowana w poście z tego wątku:

? Rozwija się do dziesiętnego statusu wyjścia ostatniego potoku (patrz Rurociągi).

Wygląda więc na to, że samo przypisanie liczy się jako polecenie (a raczej część potoku) o zerowej wartości wyjściowej, ale które ma zastosowanie przed prawą stroną przypisania (np. Wywołanie zamiany poleceń w moich przykładach tutaj).

Widzę, jak to zachowanie ma sens z praktycznego punktu widzenia, ale wydaje mi się dość niezwykłe, że samo zadanie liczyłoby się w tej kolejności. Być może, aby wyjaśnić, dlaczego jest to dla mnie dziwne, załóżmy, że zadanie było funkcją:

ASSIGNMENT( VARIABLE, VALUE )

wtedy foo="bar"byłoby

ASSIGNMENT( "foo", "bar" )

i foo=$(false)byłoby coś takiego

ASSIGNMENT( "foo", EXECUTE( "false" ) )

co oznaczałoby, że EXECUTEuruchamia się jako pierwszy, a dopiero potem ASSIGNMENT jest uruchamiany, ale nadal liczy się EXECUTEstatus.

Czy mam rację w mojej ocenie, czy coś źle zrozumiałem / czegoś mi brakuje? Czy to są właściwe powody, dla których uważam to zachowanie za „dziwne”?


1
Przepraszam, ale nie jest dla mnie jasne, co uważasz za dziwne.
Kusalananda

1
@Kusalananda Być może pomaga ci powiedzieć, że zaczęło się od pytania: „Dlaczego false;foo="bar";echo $?zawsze zwraca 0, kiedy ostatnim prawdziwym poleceniem było false?” Zasadniczo przypisania zachowują się wyjątkowo, jeśli chodzi o kody wyjścia. Ich kod zakończenia ma zawsze wartość 0, z wyjątkiem sytuacji, gdy nie dzieje się tak z powodu działania elementu po prawej stronie zadania.
phk

Odpowiedzi:


10

Status wyjścia dla zadań jest dziwny . Najbardziej oczywistym sposobem niepowodzenia przypisania jest zaznaczenie zmiennej docelowej readonly.

$ err(){ echo error ; return ${1:-1} ; }
$ PS1='$? $ '
0 $ err 42
error
42 $ A=$(err 12)
12 $ if A=$(err 9) ; then echo wrong ; else E=$? ; echo "E=$E ?=$?" ; fi
E=9 ?=0
0 $ readonly A
0 $ if A=$(err 10) ; then echo wrong ; else E=$? ; echo "E=$E ?=$?" ; fi
A: is read only
1 $

Zauważ, że nie podjęto ani prawdziwej, ani fałszywej ścieżki instrukcji if, niepowodzenie przypisania zatrzymało wykonanie całej instrukcji. bash w trybie POSIX, a ksh93 i zsh przerwie działanie skryptu, jeśli przypisanie nie powiedzie się.

Cytując tutaj standard POSIX :

Polecenie bez nazwy polecenia, ale takie, które obejmuje podstawienie polecenia, ma status wyjścia ostatniego podstawienia polecenia, które wykonała powłoka.

Jest to dokładnie część zaangażowanej gramatyki powłoki

 foo=$(err 42)

który pochodzi z simple_command(simple_command → cmd_prefix → ASSIGNMENT_WORD). Więc jeśli przypisanie się powiedzie, wówczas status wyjścia wynosi zero, chyba że nastąpiło zastąpienie polecenia, w którym to przypadku status wyjścia jest statusem ostatniego. Jeśli przypisanie nie powiedzie się, status wyjścia jest różny od zera, ale może nie być w stanie go złapać.


1
Aby dodać do twojej odpowiedzi, oto odpowiedź z innego wątku, w którym cytowany jest nowszy standard POSIX na ten temat, wniosek jest w zasadzie taki sam: unix.stackexchange.com/a/270831/117599
phk

4

Mówisz,

… 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. 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.cmd4E=D=

Ponadto, jak wskazuje Ikar , zmienne można ustawić jako tylko do odczytu. Rozważ następującą odmianę w przykładzie Iwana:

$ err() { echo "stdout $*"; echo "stderr $*" >&2; return ${1:-1}; }
$ readonly A
$ Z=$(err 41 zebra) A=$(err 42 antelope) B=$(err 43 badger)
stderr 41 zebra
stderr 42 antelope
bash: A: readonly variable
$ echo $?
1
$ printf "%s = %s\n" Z "$Z" A "$A" B "$B"
Z = stdout 41 zebra
A =
B =
$

Mimo że Ajest tylko do odczytu, bash wykonuje podstawienie polecenia po prawej stronie A=- a następnie przerywa polecenie, ponieważ Ajest tylko do odczytu. Jest to ponadto sprzeczne z twoją interpretacją, że wartość wyjściowa zadania ma zastosowanie przed prawą stroną zadania.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.