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?

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.


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.


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.


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