tymczasowa wartość skryptu bash przy komendzie

11

Jak poniżej polecenia,

if true; then
   IFS=":" read a b c d e f <<< "$test"

Książka mówi, że gdy polecenie przypisania wartości ( IFS ":") jest używane przed poleceniem głównym ( read a b c d e f <<< "$value"), jego wartość obowiązuje tymczasowo dla polecenia głównego. Tak więc readpolecenie używa ogranicznika :.

Ale, podobnie jak to polecenie,

if true; then
   HOME="hello" echo "$HOME"

Wiadomość echa nie jest cześć. Jakie jest prawdziwe znaczenie powyższego polecenia?

A.Cho
źródło

Odpowiedzi:

5

Sprowadza się to do pytania, jak działa ewaluacja. Oba przykłady działają w ten sam sposób, problem występuje z powodu tego, jak powłoka (bash, tutaj) rozwija zmienne.

Kiedy napiszesz to polecenie:

HOME="foo" echo $HOME

$HOMERozpręża przed uruchomieniem polecenia . Dlatego jest on rozwijany do pierwotnej wartości, a nie nowej, dla której ustawiono ją dla polecenia. HOMEZmienna rzeczywiście została zmieniona w środowisku, że echokomenda jest uruchomiony w jednak jesteś drukowanie $HOMEod rodzica.

Aby to zilustrować, rozważ to:

$ HOME="foo" bash -c 'echo $HOME'
foo
$ echo $HOME
/home/terdon

Jak widać powyżej, pierwsze polecenie drukuje tymczasowo zmienioną wartość, HOMEa drugie drukuje oryginał, pokazując, że zmienna została zmieniona tylko tymczasowo. Ponieważ bash -c ...polecenie jest ujęte w pojedyncze cudzysłowy ( ' ') zamiast podwójnych ( " "), zmienna nie jest rozwijana i jest przekazywana w stanie niezmienionym do nowego procesu bash. Ten nowy proces następnie go rozwija i drukuje nową wartość, dla której został ustawiony. Możesz to zobaczyć, jeśli użyjesz set -x:

$ set -x
$ HOME="hello" echo "$HOME"
+ HOME=hello         
+ echo hello
hello

Jak widać powyżej, zmienna $HOME nigdy nie jest przekazywana do echo. Widzi tylko swoją rozszerzoną wartość. Porównać z:

$ HOME="hello" bash -c 'echo $HOME'
+ HOME=hello
+ bash -c 'echo $HOME'
hello

Tutaj, ze względu na pojedyncze cudzysłowy, zmienna, a nie jej wartość, są przekazywane do nowego procesu.

terdon
źródło
7

Kiedy powłoka parsuje linię, tokenizuje linię na słowa, wykonuje różne rozwinięcia (w kolejności) słów, a następnie wykonuje polecenie.

Przypuszczać test=1:2:3:4:5:6

Spójrzmy na to polecenie: IFS=":" read a b c d e f <<< "$test"

Po tokenizacji następuje rozszerzenie parametrów :IFS=":" read a b c d e f <<< "1:2:3:4:5:6"

Powłoka ustawi zmienną IFS na czas trwania polecenia odczytu i readwie, jak zastosować $ IFS do swoich danych wejściowych i podać wartości nazwom zmiennych.

To polecenie ma podobną historię, ale inny wynik: HOME="hello" echo "$HOME"

Ponieważ rozszerzanie parametrów odbywa się przed uruchomieniem polecenia, powłoka ma:

HOME="hello" echo "/home/username"

A następnie, podczas wykonywania polecenia echo, nowa wartość $ HOME nie jest w ogóle używana.

Aby osiągnąć to, co próbujesz zrobić, wybierz jedną z opcji

# Delay expansion of the variable until its new value is set
HOME="hello" eval 'echo "$HOME"'

lub

# Using a subshell, so the altered env variable does not affect the parent.
# The semicolon means that the variable assignment will occur before
# the variable expansion
(HOME="hello"; echo "$HOME")

ale nie wybieraj pierwszego.

Glenn Jackman
źródło
Prawdopodobnie lepiej wybrać pierwszy. Przynajmniej jest znacznie szybszy. Kiedy eval jest odpowiedzią, czasami prawdopodobnie zadajesz złe pytanie. Ale jeśli ktoś musi to zrobić z jakichś powodów, zmiana odpowiedzi nie czyni samego pytania mniej złym. Innym rozwiązaniem jest zawinięcie go w funkcję i użycie local.
user23013
Dlaczego należy evalunikać tego rozwiązania?
DarkHeart
Jeśli nie kontrolujesz ściśle danych wejściowych, musisz bardzo uważać na kod, który pozwala innym osobom na wstrzykiwanie do twojego programu.
glenn jackman
-1

Istnieją dwa zakresy: zmienne środowiskowe i zmienne lokalne. Zmienne środowiskowe są poprawne dla każdego procesu (patrz setenv, getenv), podczas gdy zmienne lokalne są aktywne tylko w tej sesji powłoki. (To nie jest oczywiste rozróżnienie ...)

Implikowane env(jak w twoim przykładzie) modyfikuje środowisko, podczas gdy echo ...używa lokalnych - więc envnie ma żadnego efektu.

Aby zmodyfikować zmienne lokalne, użyj, powiedzmy,

( HOME="foo" ; echo "$HOME" )

Tutaj nawiasy określają zakres tego zadania.

Andrew Miloradovsky
źródło
1
Nie ma to nic wspólnego ze zmiennym zakresem, problem polega na tym, że zmienna jest rozwijana przed przekazaniem jej do powłoki potomnej.
terdon