W bash, jak zdefiniować zmienną „zakresu poleceń”?


6

Czytam tę książkę o architekturze bash, a jeden z jej opiekunów mówi:

Istnieją różne zakresy zmiennych dla wywołań funkcji powłoki i zakresy tymczasowe dla zmiennych ustawianych przez instrukcje przypisania poprzedzające polecenie . Gdy na przykład te instrukcje przypisania poprzedzają polecenie wbudowane w powłokę, powłoka musi śledzić prawidłową kolejność rozwiązywania odwołań do zmiennych, a połączone zakresy pozwalają bash to zrobić.

Nie mogę wymyślić, jak to zrobić. Spowoduje to wydrukowanie pustej nowej linii:

foo="bar" echo $foo

Wyświetla to „pasek”, ale foozmienna jest nadal definiowana po zakończeniu polecenia, więc nie uważam, aby był to „zakres polecenia”:

foo="bar"; echo $foo

Jak zdefiniować zmienną z zakresem poleceń?

Odpowiedzi:


6

Twoje pierwsze przypuszczenie było słuszne, ale użyłeś złego przykładu. Gdy powłoka odczytuje polecenie

foo=bar echo $foo

pierwszą rzeczą, jaką robi (lub przynajmniej jedną z pierwszych rzeczy) jest poszukiwanie referencji zmiennych (jak $foo) i ich ocena.  Następnie przetwarza to, co zostało z linii. (Może to być trochę nadmierne uproszczenie; szczegółowe informacje można znaleźć bash(1)w Instrukcji obsługi Bash ). Masz więc polecenie

echo (nic)

biegać z foo=bar; ale jest za późno; nie ma już nic, na co można spojrzeć $foo. Możesz to zademonstrować za pomocą następującej sekwencji:

$ foo=oldvalue
$ foo=bar echo "$foo"
oldvalue

Ale

polecenie foo = bar

jest droga. Oto kilka przykładów, które działają:

foo=bar sh -c 'echo "$foo"'

i

foo=bar env

(Możesz to przepuścić grep foo).

Właśnie zauważyłem cytowane zdanie: „Gdy te instrukcje przypisania poprzedzają polecenie wbudowane w powłokę, na przykład…”, sugerując, że wbudowane polecenia (takie jak echo) reprezentują szczególny przypadek. Jest dla mnie intuicyjne (przynajmniej dla mnie), że ten „zakres poleceń”, którego szukasz, musiałby działać, ustawiając zmienną środowiskową w procesie potomnym, i nie jest jasne, jak by to działało dla wbudowanego polecenia, który nie działa w procesie potomnym. Niewiele wbudowanych poleceń ma bezpośredni dostęp do środowiska, a ich interakcja ze zmiennymi powłoki jest czasami tajemna. Ale wymyśliłem kilka innych przykładów, które mogą być bardziej tym, czego szukasz:

foo=bar eval 'echo $foo'

wyświetli „pasek”, ponieważ ocena $foojest odroczona. Jest on obsługiwany przez eval(wbudowane) polecenie, a nie przez początkowe polecenie parsowania powłoki. Lub utwórz plik tekstowy o nazwie (na przykład) showme.sh. Umieść w nim echo $foo(lub echo "$foo") polecenie i powiedz

foo=bar . showme.sh

Prawdopodobnie o tym mówi Twoja książka, gdy mówi: „… powłoka musi śledzić prawidłową kolejność rozwiązywania odwołań do zmiennych…”. W jakiś sposób powłoka uruchamia polecenia showme.shz foorówną bar, a następnie powraca do poprzednia wartość foo(jeśli istnieje), gdy wraca do pierwotnego wejścia (terminala).


Mówisz, że to nie działa, ponieważ echo jest wbudowanym poleceniem? Jeśli tak, uważam, że powinieneś to podkreślić inaczej. Nie jestem pewien, czy to główne wynos.
Daniel Kaplan

1
Nie, mówię to samo, co mówi grawitacja - nie działa, ponieważ $foo„rozszerza się”, zanim cokolwiek innego się wydarzy. Zaktualizowałem odpowiedź, dodając więcej przemyśleń na temat wbudowanych poleceń.
G-Man

1
@DocSalvager: Do tego miałem na myśli, kiedy powiedziałem: „Niewiele wbudowanych poleceń ma bezpośredni dostęp do środowiska”, ale myślę, że mógłbym to wyrazić jaśniej.
G-Man,

1
echo '$foo'wyprowadza ciąg $foo. cat '$foo'i ls -l '$foo’poszukaj pliku o nazwie $foo. grep '$foo' .bashrcszuka ciągu $foow twoim .bashrcpliku. Ale powłoka, w przeciwieństwie do innych programów, traktuje $specjalnie; sh -c '$foo'szuka zmiennej środowiskowej o nazwie foo. Na przykład foo=date sh -c '$foo'uruchomi datepolecenie. (Porównaj z faktem, że cat '\0107'i ls -l '\0107’poszukaj pliku o nazwie \0107i grep '\0107'wyszukuje ciąg \0107, ale echo -e '\0107'wyprowadza ciąg G,… ( ciąg dalszy)
G-Man

1
(Ciąg dalszy)… ponieważ echotraktuje \specjalnie, jeśli -eopcja jest podana.)
G-Man

5

Problem polega na tym, że rozwinięcie zmiennej ( $foo→ wartość) następuje przed oceną całego polecenia (włóż foo=bardo środowiska, uruchom echo ...).

Będzie działać dla poleceń, które same uzyskują dostęp do środowiska:

foo=bar env | grep ^foo

foo=bar declare -p foo

Bash tak naprawdę nie ma odpowiedniego zakresu dla tego, czego szukasz; musisz użyć sztuczki „podprocesu”, aby uzyskać nowy proces dla polecenia. (Oczywiście oznacza to, że zakres jest tylko do odczytu, żadne zmiany nie dotrą do elementu nadrzędnego ...) Użyj, ( ... )aby utworzyć podpowłokę:

(foo=bar; echo $foo)
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.