Rozważ ten fragment:
$ SOMEVAR=AAA
$ echo zzz $SOMEVAR zzz
zzz AAA zzz
Tutaj mam ustawione $SOMEVAR
aby AAA
na pierwszej linii - a kiedy echo go na drugiej linii, otrzymuję AAA
zawartość zgodnie z oczekiwaniami.
Ale jeśli spróbuję określić zmienną w tym samym wierszu poleceń, co echo
:
$ SOMEVAR=BBB echo zzz $SOMEVAR zzz
zzz AAA zzz
... nie dostaję BBB
tak jak oczekiwałem - otrzymuję starą wartość ( AAA
).
Czy tak właśnie powinno być? Jeśli tak, to dlaczego możesz określić zmienne, takie jak LD_PRELOAD=/... program args ...
i sprawić, by działały? czego mi brakuje?
bash
environment-variables
echo
sdaau
źródło
źródło
LD_PRELOAD
działa to, że zmienna jest ustawiona w programie na środowisko - nie na swojej linii komend.Odpowiedzi:
To, co widzisz, to oczekiwane zachowanie. Problem polega na tym, że powłoka macierzysta ocenia
$SOMEVAR
w wierszu poleceń, zanim wywoła polecenie ze zmodyfikowanym środowiskiem. Musisz uzyskać ocenę$SOMEVAR
odroczoną do momentu ustawienia środowiska.Twoje bezpośrednie opcje obejmują:
SOMEVAR=BBB eval echo zzz '$SOMEVAR' zzz
.SOMEVAR=BBB sh -c 'echo zzz $SOMEVAR zzz'
.Oba używają pojedynczych cudzysłowów, aby zapobiec wartościowaniu przez powłokę macierzystą
$SOMEVAR
; jest oceniany dopiero po ustawieniu go w środowisku (tymczasowo na czas trwania pojedynczego polecenia).Inną opcją jest użycie notacji podpowłoki (co również zasugerował Marcus Kuhn w swojej odpowiedzi ):
(SOMEVAR=BBB; echo zzz $SOMEVAR zzz)
Zmienna jest ustawiana tylko w podpowłoce
źródło
Problem ponownie odwiedzony
Szczerze mówiąc, podręcznik jest niejasny w tej kwestii. Podręcznik GNU Bash mówi:
Jeśli naprawdę przeanalizujesz zdanie, to mówi, że środowisko polecenia / funkcji jest zmodyfikowane, ale nie środowisko dla procesu nadrzędnego. Więc to zadziała:
ponieważ środowisko dla polecenia env zostało zmodyfikowane przed jego wykonaniem. Jednak to nie zadziała:
$ set -x; TESTVAR=bbb echo aaa $TESTVAR ccc + TESTVAR=bbb + echo aaa ccc aaa ccc
z powodu tego, kiedy interpretacja parametrów jest wykonywana przez powłokę.
Kroki tłumacza
Inną częścią problemu jest to, że Bash definiuje następujące kroki dla swojego interpretera:
To, co się tutaj dzieje, to fakt, że elementy wbudowane nie otrzymują własnego środowiska wykonawczego, więc nigdy nie widzą zmodyfikowanego środowiska. Ponadto, proste polecenia (np / bin / echo) mają otrzymać zmodyfikowany ennvironment (dlatego przykładem env pracował), ale rozszerzenie powłoki odbywa się w bieżącym środowisku w kroku # 4.
Innymi słowy, nie przekazujesz „aaa $ TESTVAR ccc” do / bin / echo; przekazujesz interpolowany ciąg (rozszerzony w bieżącym środowisku) do / bin / echo. W tym przypadku, ponieważ bieżące środowisko nie ma TESTVAR , po prostu przekazujesz komendę „aaa ccc”.
Podsumowanie
Dokumentacja mogłaby być dużo bardziej przejrzysta. Dobrze, że jest przepełnienie stosu!
Zobacz też
http://www.gnu.org/software/bash/manual/bashref.html#Command-Execution-Environment
źródło
FOO=foo eval 'echo $FOO'
drukujefoo
zgodnie z oczekiwaniami. Oznacza to, że możesz robić takie rzeczy jakIFS="..." read ...
.Aby osiągnąć to, co chcesz, użyj
( SOMEVAR=BBB; echo zzz $SOMEVAR zzz )
Powód:
Musisz oddzielić przypisanie średnikiem lub nową linią od następnego polecenia, w przeciwnym razie nie zostanie ono wykonane przed interpretacją parametrów dla następnego polecenia (echo).
Musisz dokonać przypisania w środowisku podpowłoki , aby upewnić się, że nie będzie trwał poza bieżącą linią.
To rozwiązanie jest krótsze, schludniejsze i bardziej wydajne niż niektóre inne sugerowały, w szczególności nie tworzy nowego procesu.
źródło
(export SOMEVAR=BBB; python -c "from os import getenv; print getenv('SOMEVAR')")
SOMEVAR=BBB python -c "from os import getenv; print getenv('SOMEVAR')"
Powodem jest to, że ustawia to zmienną środowiskową dla jednej linii. Ale
echo
nie robi rozszerzenia,bash
robi. W związku z tym zmienna jest faktycznie rozwijana przed wykonaniem polecenia, nawet jeśliSOME_VAR
znajduje sięBBB
w kontekście polecenia echo.Aby zobaczyć efekt, możesz zrobić coś takiego:
$ SOME_VAR=BBB bash -c 'echo $SOME_VAR' BBB
Tutaj zmienna nie jest rozwijana, dopóki nie zostanie wykonany proces potomny, więc zobaczysz zaktualizowaną wartość. jeśli
SOME_VARIABLE
ponownie sprawdzisz w powłoce nadrzędnej, nadal jestAAA
, zgodnie z oczekiwaniami.źródło
SOMEVAR=BBB; echo zzz $SOMEVAR zzz
Użyć ; aby oddzielić instrukcje, które znajdują się w tym samym wierszu.
źródło
LD_PRELOAD
i takie mogą działać przed plikiem wykonywalnym bez średnika, choć ... Jeszcze raz wielkie dzięki - brawa!Oto jedna alternatywa:
SOMEVAR=BBB && echo zzz $SOMEVAR zzz
źródło
&&
czy;
oddzielasz je, przypisanie pozostaje niezmienione, co nie jest pożądanym zachowaniem OP. Markus Kuhn ma poprawną wersję tej odpowiedzi.