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ń?

Daniel Kaplan
źródło

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).

G-Man
źródło
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)
grawitacja
źródło