Automatyczne rozszerzanie zmiennych w poleceniu bash [[]]


13

Podczas wyłuskiwania zmiennej bashmusisz użyć $znaku. Niemniej jednak wydaje się, że następujące działa dobrze:

x=5
[[ x -gt 2 ]]

Czy ktoś może to wyjaśnić?

Edycja: (więcej informacji)

Chodzi mi o to, w jaki sposób i dlaczego polecenie [[]] usuwa dane z mojej zmiennej x bez znaku $. I tak, jeśli x = 1, instrukcja jest oceniana na false (zwracany status 1)


2
Co masz na myśli mówiąc „dobrze działa”? A czy twoja ocena zmieni się, jeśli x=1następnie [[ x -gt 2]]?
nohillside

Mam na myśli: jak i dlaczego polecenie [[]] usuwa dane z mojej zmiennej x bez znaku $. I tak, jeśli x = 1, instrukcja jest fałszywa (zwraca status 1)
Gość

Odpowiedzi:


9

Powodem jest to, że -eqwymusza arytmetyczną ocenę argumentów.

Operator arytmetyczna: -eq, -gt, -lt, -ge, -lei -newewnątrz [[ ]](w KSH) i zsh, środki do automatycznego rozszerzania nazwy zmiennych jak w języku C, nie potrzeba czołowym $.

  • W celu potwierdzenia musimy zajrzeć do kodu źródłowego bash. Instrukcja nie oferuje bezpośredniego potwierdzenia.

    Wewnątrz test.cprzetwarzania operatorów arytmetycznych można znaleźć tę funkcję:

    arithcomp (s, t, op, flags)

    Gdzie si tsą oba operandy. Operandy są przekazywane do tej funkcji:

    l = evalexp (s, &expok);
    r = evalexp (t, &expok);

    Funkcja evalexpjest zdefiniowana wewnątrz expr.c, która ma następujący nagłówek:

    /* expr.c -- arithmetic expression evaluation. */

    Tak więc, obie strony operatora arytmetycznego popadają (bezpośrednio) w ocenę wyrażeń arytmetycznych. Bezpośrednio, nie ma ale, nie ma.


W praktyce z:

 $ x=3

Oba te niepowodzenia:

 $ [[ x = 4 ]] && echo yes || echo no
 no

 $ [[ x = 3 ]] && echo yes || echo no
 no

Co jest poprawne, xnie jest rozwijane i xnie jest równe liczbie.

Jednak:

 $ [[ x -eq 3 ]] && echo yes || echo no
 yes

 $ [[ x -eq 4 ]] && echo yes || echo no
 no

Zmienna o nazwie xzostaje rozwinięta (nawet bez $).

Nie dzieje się tak […]w przypadku zsh ani bash (dzieje się to w ksh).


To samo co dzieje się w $((…)):

 $ echo $(( x + 7 ))
 10

I proszę zrozumieć, że jest to (bardzo) rekurencyjne (z wyjątkiem myślnika i yash):

 $ a=b b=c c=d d=e e=f f=3
 $ echo "$(( a + 7 ))" 
 10

A 😮

I dość ryzykowne:

 $ x='a[$(date -u)]'
 $ [[ x -eq 3 ]] && echo yes || echo no
 bash: Tue Dec  3 23:18:19 UTC 2018: syntax error in expression (error token is "Dec  3 23:18:19 UTC 2018")

Błąd składniowy można łatwo uniknąć:

 $ a=3; x='a[$(date -u >/dev/tty; echo 0)]'

 $ [[ x -eq 3 ]] && echo yes || echo no
 Tue Dec  4 09:02:06 UTC 2018
 yes

Jak mówi przysłowie: odkaż swój wkład

 $ [[ ${x//[^0-9]} -eq 3 ]] && echo yes || echo no
 no

koniec 😮


Zarówno (starsze) zewnętrzne /usr/bin/test(nie wbudowane test), jak i jeszcze starsze, a także zewnętrzne exprnie rozwijają wyrażeń tylko liczb całkowitych (i najwyraźniej tylko liczb dziesiętnych):

 $ /usr/bin/test "x" -eq 3
 /usr/bin/test: invalid integer x

 $ expr x + 3
 expr: non-integer argument

Ciekawy. Nietrudno powiedzieć, jak to możliwe - jako [[słowo kluczowe operatory i operandy są wykrywane po odczytaniu polecenia, a nie po rozwinięciu. W ten sposób [[można leczyć -eqw bardziej inteligentny sposób niż, powiedzmy [. Zastanawiam się jednak: gdzie możemy znaleźć dokumentację dotyczącą logiki używanej przez bash do interpretacji poleceń złożonych? Nie wydaje mi się to dość oczywiste i najwyraźniej nie jestem w stanie znaleźć satysfakcjonujących wyjaśnień w manlub info bash.
fra-san

Bash nie dokumentuje tego nigdzie, gdzie mogę znaleźć. Jest to rodzaj opisu w człowieka ksh93 : Następujące nieaktualne porównania arytmetyczne dozwolone są także: EXP1 -eq EXP2 . Jest to tekst w tej testczęści człowieka zshbuiltins operatorów arytmetycznych oczekiwać całkowitą argumentów zamiast wyrażeń arytmetycznych . Co potwierdza, że ​​niektóre argumenty są traktowane jako wyrażenia arytmetyczne przez wbudowany test w warunkach nieokreślonych w tym cytacie. Potwierdzę kodem źródłowym….…
NotAnUnixNazi

7

Operandy porównań liczbowych -eq, -gt, -lt, -ge, -lei -nesą traktowane jako wyrażeń arytmetycznych. Z pewnymi ograniczeniami nadal muszą być słowami pojedynczymi.

Zachowanie nazw zmiennych w wyrażeniach arytmetycznych opisano w Shell Arithmetic :

Zmienne powłoki są dozwolone jako operandy; interpretacja parametru jest wykonywana przed obliczeniem wyrażenia. W wyrażeniu zmienne powłoki mogą być również przywoływane według nazwy bez użycia składni rozszerzania parametrów. Zmienna powłoki, która jest zerowa lub nieustawiona, przyjmuje wartość 0, gdy odwołuje się do niej nazwa bez użycia składni interpretacji parametrów.

i również:

Wartość zmiennej jest oceniana jako wyrażenie arytmetyczne, gdy się do niej odwołuje

Ale tak naprawdę nie mogę znaleźć części dokumentacji, w której jest powiedziane, że porównania numeryczne przyjmują wyrażenia arytmetyczne. Nie jest to opisane w Konstrukcjach warunkowych pod [[, ani nie jest opisane w Wyrażeniach warunkowych Bash .

Ale eksperymentalnie wydaje się, że działa jak wspomniano powyżej.

Więc takie rzeczy działają:

a=6
[[ a -eq 6 ]] && echo y 
[[ 1+2+3 -eq 6 ]] && echo y
[[ "1 + 2 + 3" -eq 6 ]] && echo y

to też (wartość zmiennej jest oceniana):

b='1 + 2 + 3'
[[ b -eq 6 ]] && echo y

Ale tak nie jest; nie jest to pojedyncze słowo powłoki podczas [[ .. ]]analizowania, więc występuje błąd składniowy w warunkowym:

[[ 1 + 2 + 3 -eq 6 ]] && echo y

W innych kontekstach arytmetycznych wyrażenie nie musi być spacją. Wypisuje się 999, gdy nawiasy jednoznacznie ograniczają wyrażenie arytmetyczne w indeksie:

a[6]=999; echo ${a[1 + 2 + 3]}

Z drugiej strony =porównanie jest dopasowaniem wzorca i nie obejmuje arytmetyki, ani automatycznego rozszerzania zmiennych wykonywanego w kontekście arytmetycznym (konstrukcje warunkowe):

Gdy używane są operatory ==i !=, ciąg po prawej stronie operatora jest uważany za wzorzec i dopasowywany zgodnie z zasadami opisanymi poniżej w Dopasowywanie wzorców, tak jakby opcja powłoki extglob była włączona. =Operator jest identyczna ==.

Jest to więc fałsz, ponieważ ciągi znaków są oczywiście różne:

[[ "1 + 2 + 3" = 6 ]] 

jak to jest, mimo że wartości liczbowe są takie same:

[[ 6 = 06 ]] 

i tutaj również porównywane są ciągi ( xi 6), są one różne:

x=6
[[ x = 6 ]]

Spowoduje to jednak rozwinięcie zmiennej, więc jest to prawdą:

x=6
[[ $x = 6 ]]

nie mogę znaleźć części dokumentacji, w której jest powiedziane, że porównania numeryczne przyjmują wyrażenia arytmetyczne. Potwierdzenie znajduje się w kodzie .
NotAnUnixNazi,

Najbliższe jest to, że opis arg1 OP arg2mówi, że argumenty mogą być dodatnimi lub ujemnymi liczbami całkowitymi, co, jak sądzę, powinno sugerować, że są one traktowane jako wyrażenia arytmetyczne. Myląco oznacza to również, że nie mogą być zerowe. :)
Barmar,

@Barmar, ehh, racja. Ale dotyczy to również porównań numerycznych [i nie są to wyrażenia arytmetyczne. Zamiast tego Bash narzeka na wartości całkowite.
ilkkachu

@ilkkachu [jest poleceniem zewnętrznym, nie ma dostępu do zmiennych powłoki. Często jest zoptymalizowany za pomocą wbudowanego polecenia, ale nadal zachowuje się tak samo.
Barmar

@Barmar, miałem na myśli to, że wyrażenie „Arg1 i arg2 mogą być dodatnimi lub ujemnymi liczbami całkowitymi”. pojawia się w Wyrażeniach warunkowych Bash , a ta lista dotyczy [również [[. Nawet z [operandami -eqi przyjaciółmi są / muszą być liczbami całkowitymi, więc ten opis również ma zastosowanie. Przyjmowanie „musi być liczbami całkowitymi”, co oznacza „są interpretowane jako wyrażenia arytmetyczne”, nie ma zastosowania w obu przypadkach. (Prawdopodobnie przynajmniej częściowo z powodu [działania jak zwykłe polecenie, jak mówisz.)
ilkkachu

1

Tak, twoja obserwacja jest prawidłowa, interpretacja zmiennych odbywa się na wyrażeniach w podwójnych nawiasach [[ ]], więc nie musisz umieszczać $przed nazwą zmiennej.

Jest to wyraźnie określone w bashinstrukcji:

[[ wyrażenie ]]

(...) Podział słów i interpretacja nazw ścieżek nie są wykonywane na słowach pomiędzy [[a]]; Wykonywane jest interpretacja tyldy, interpretacja parametrów i zmiennych, interpretacja arytmetyczna, podstawianie poleceń, podstawianie procesów i usuwanie cytatów.

Zauważ, że nie dotyczy to wersji z pojedynczym nawiasiem [ ], ponieważ [nie jest słowem kluczowym powłoki (składnią), ale raczej poleceniem (w bash jest wbudowane, inne powłoki mogą używać zewnętrznego, liniowanego do testowania).


1
Dziękuję za odpowiedź. Wygląda na to, że działa to tylko dla liczb. x = miasto [[$ x == miasto]] To nie działa bez znaku $.
Gość

3
Wygląda na to, że jest tu więcej: (x=1; [[ $x = 1 ]]; echo $?)zwraca 0, (x=1; [[ x = 1 ]]; echo $?)zwraca 1, tzn. Nie xporównuje się interpretacji parametrów podczas porównywania ciągów. To zachowanie wygląda jak ocena arytmetyczna wywołana rozszerzeniem arytmetycznym, czyli tym, co się dzieje (x=1; echo $((x+1))). (O ocenie arytmetycznej man bashstwierdza: „W wyrażeniu do zmiennych powłoki można również odwoływać się według nazwy bez użycia składni interpretacji parametrów).
fra-san

@ fra-san Rzeczywiście, ponieważ -gtoperator oczekuje liczby, więc całe wyrażenie jest ponownie oceniane tak, jakby było w środku (()), z drugiej strony ==oczekuje łańcuchów, więc zamiast tego uruchamiana jest funkcja dopasowania wzorca. Nie zagłębiałem się w kod źródłowy, ale brzmi rozsądnie.
jimmij

[jest powłoką wbudowaną w bash.
Nizam Mohamed

1
@NizamMohamed To jest wbudowane, ale wciąż nie jest słowem kluczowym.
Kusalananda
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.