Dlaczego „A = 10 echa $ A” nie drukuje 10?

25

To polecenie:

A=10 echo $A

wypisuje pustą linię. Dlaczego nie 10? Dlaczego ustawienie tymczasowego środowiska w miejscu nie działa?

Chcę poznać przyczynę i wyjaśnienie, a nie rozwiązanie.

użyłem

LANG=C gcc ...

aby wymusić na gcc użyj języka zastępczego (angielski) zamiast języka systemowego (chiński). Zakładam więc, że VAR=valueprefiks skonfiguruje tymczasowe środowisko dla następującego po nim polecenia. Ale wygląda na to, że mam jakieś nieporozumienie.

Earth Engine
źródło

Odpowiedzi:

22

Jest to kwestia kolejności, w jakiej występują różne etapy oceny polecenia.

A=10 echo $Anajpierw analizuje polecenie w proste polecenie złożone z trzech słów A=10, echooraz $A. Następnie każde słowo podlega podstawieniu zmiennej, tj. Przekształceniu rozszerzeń zmiennych, na przykład $Ana ich wartości (pomijam kroki, które nic nie robią).

Jeśli Ama wartość foopoczątkowo, wynik z etapów ekspansji jest prosta komenda, która nadal ma trzy słowa: A=10, echoi foo. (Powłoka pamięta również w tym momencie, które znaki były początkowo w cudzysłowie - w tym przypadku brak.) Kolejnym krokiem jest wykonanie polecenia. Ponieważ A=10zaczyna się od prawidłowej nazwy zmiennej, po której następuje znak równości, jest traktowany jako przypisanie; zmienna Ajest ustawiona 10zarówno w powłoce, jak i środowisku podczas wykonywania polecenia. (Zwykle musisz pisać, export Aaby mieć Aw środowisku, a nie tylko jako zmienną powłoki; jest to wyjątek). Następne słowo nie jest przypisaniem, więc jest traktowane jako nazwa polecenia (jest to polecenie wbudowane). Theechopolecenie nie zależy od żadnej zmiennej, więc A=10 echo $Ama dokładnie taki sam efekt jak echo $A.

Jeśli chcesz ustawić zmienną tylko na czas trwania polecenia, ale biorąc pod uwagę przypisanie podczas wykonywania polecenia, możesz użyć podpowłoki. Podpowłoka, wskazana w nawiasach, powoduje, że wszystkie zmiany stanu (przypisania zmiennych, katalog bieżący, definicje funkcji itp.) Są lokalne w podpowłoce.

(A=10; echo $A)

Zrób to, export A=10jeśli chcesz wyeksportować zmienną do środowiska, aby była widoczna dla programów zewnętrznych.

Gilles „SO- przestań być zły”
źródło
Dzięki, mogę powiedzieć A=10 (echo $A)i dostać 10?
Earth Engine
2
@EarthEngine Nie, to byłby błąd składniowy. Przypisanie musi znajdować się na początku prostej komendy (tj. Po prostu nazwa komendy i niektóre parametry oraz opcjonalnie niektóre początkowe przypisania i niektóre przekierowania). A=10; (echo $A)wyjścia, 10ale także zestawy Adla pozostałej części skryptu.
Gilles „SO- przestań być zły”
2
@EarthEngine Ale możesz powiedzieć A=10 eval 'echo $A'. Pojedyncze cudzysłowy przestają $Abyć interpretowane, dopóki nie zostanie oceniony cały wiersz ... Do tego czasu A = 10. Uważam tę odpowiedź za bardziej słuszną niż zaakceptowaną.
Oli
Myślę, że to prawidłowe wyjaśnienie. Przyczyną takiego zachowania jest kolejność, w której ma miejsce ekspansja $Ai przypisanie A. Np. Nie A=5; A=6 let 'a=A'; echo $azwraca i nie sądzę, że uruchamia podpowłokę, ponieważ jest to wbudowane polecenie. 65let
David Ongaro
@EarthEngine: Gdy powiedziane jest, że poprawne wyjaśnienie jest kolejnością oceny, może to być mylące: nieA=10 echo $A będzie później ustawiane dla żadnych poleceń, nawet jeśli będą w różnych wierszach (gdy wyraźnie zadanie zostało już ocenione). Nie chodzi o porządek, chodzi o zakresA=10
MestreLion
37

Podczas korzystania z LANG=C gcc ... tego, co się dzieje, jest to, że powłoka ustawia LANG dla gcc„s środowiska tylko , a nie dla aktualnej samego środowiska ( patrz uwaga ). Więc po gcczakończeniu LANGpowraca do poprzedniej wartości (lub jest wyłączony).

Dodatkowo, gdy A=10 echo $Ago używasz , to powłoka zastępuje $ A, a nie echo, a to podstawienie (zwane „rozszerzeniem”) następuje przed oceną instrukcji (w tym przypisaniem), więc aby działać zgodnie z oczekiwaniami A, wartość musi być już ustawiona w obecnym środowisku przed tym stwierdzeniem.

Dlatego A=10 echo $Anie działa zgodnie z oczekiwaniami: A=10zostanie ustawiony na echo, ale echo wewnętrznie ignoruje wartość zmiennej środowiskowej A. I $Ajest zastępowany wartością ustawioną w bieżącej powłoce (która nie jest żadna), a następnie przekazywany jako argument do echa.

Więc założenie jest poprawne: VAR=value command ma pracy, ale jest to tylko w przypadku, commandwewnętrznie używa var. Jeśli nie, to nadal można przekazać valuejako argumentu do command, ale argumenty są zastępowane przez obecnego powłoki, więc muszą być ustawione przed użytkowania:VAR=value; command "$VAR"

Jeśli wiesz, jak utworzyć skrypt wykonywalny, możesz wypróbować to jako test:

#!/bin/sh
echo "1st argument is $1"
echo "A is $A"

Zapisz jako testscripti spróbuj:

$ A=5; A=10 testscript "$A"; echo "$A"
1st argument is 5
A is 10
5

Na koniec warto poznać różnicę między zmiennymi powłoki a zmiennymi środowiskowymi i argumentami programu .

Oto kilka dobrych referencji:

.

(*) Uwaga: technicznie powłoka ma ustawiony w obecnym środowisku też, a oto dlaczego: Niektóre polecenia, jak echo, readi testpowłoki builtins , i jako takie nie tarło proces potomny. Działają w obecnym środowisku. Ale powłoka dba o to, aby przypisanie trwało tylko do momentu uruchomienia polecenia, więc dla wszystkich praktycznych celów efekt jest taki sam: przypisanie jest widoczne tylko dla tego pojedynczego polecenia.

MestreLion
źródło
2
To wyjaśnienie jest w rzeczywistości niepoprawne, chociaż prowadzi do prawidłowego wniosku we wszystkich przypadkach z wyjątkiem kilku narożnych. Prawdziwe wyjaśnienie to kolejność rozwinięcia: $Ajest oceniane przed przypisaniem. Myślę, że twoje wyjaśnienie nie powiedzie się tylko w przypadku zwykłych wbudowanych narzędzi, których zachowanie zależy od wartości zmiennej: wbudowane widzi przypisaną wartość. Typowym przykładem jest IFS=: read one two three restodczytywanie pól oddzielonych dwukropkami: readwbudowane widzi wartość IFS.
Gilles „SO - przestań być zły”
„Nie dla samej bieżącej powłoki” jest błędne: zmienna jest ustawiona w bieżącej powłoce, ale wystarcza tylko na bieżące proste polecenie. echoby zobaczyć wartość 10dla A, gdyby zależało.
Gilles „SO- przestań być zły”
@Gilles: wielkie dzięki za wyjaśnienia! Nie byłem świadomy tej subtelności. Tak więc, jeśli dobrze zrozumiałem, bash musi ustawić dla bieżącego środowiska, w przeciwnym razie wbudowane (które nie odradzają nowego pid) nie zobaczyłyby przypisania, tak jak inne polecenia „regurlar”. Ale resetuje się po wykonaniu polecenia, aby ograniczyć zakres przypisania. Czy to prawda, odpowiednio naprawię moją odpowiedź. PS: pomijając A=10 test; echo $A
kwestie
3

Jednym prawdopodobnie czystym sposobem na zrobienie tego, co najwyraźniej chcesz, jest wydanie polecenia:

A=10 eval 'echo $A'

Które w efekcie odroczą podstawienie wartości 10 w miejsce $ A do późniejszego kontekstu (tj. „Wewnątrz” eval, który już wie o przypisaniu). Pamiętaj, że pojedyncze cudzysłowy są niezbędne. Taki konstrukt w czysty sposób przekazuje przypisanie do żądanego polecenia (w tym przypadku echa) bez ryzyka zanieczyszczenia środowiska.

nhokka
źródło