Chcę dynamicznie przypisywać wartości do zmiennych za pomocą eval
. Działa następujący przykładowy manekin:
var_name="fruit"
var_value="orange"
eval $(echo $var_name=$var_value)
echo $fruit
orange
Jednak gdy wartość zmiennej zawiera spacje, eval
zwraca błąd, nawet jeśli $var_value
jest wstawiany między podwójnymi cudzysłowami:
var_name="fruit"
var_value="blue orange"
eval $(echo $var_name="$var_value")
bash: orange : command not found
Jest jakiś sposób na obejście tego?
eval
ten sposób jest złe. Rozwijasz się$var_value
przed przekazaniem go,eval
co oznacza, że będzie interpretowany jako kod powłoki! (spróbuj na przykład zvar_value="';:(){ :|:&};:'"
)eval
(co jest jednym z powodów, dla których powiedziałem, że nie powinieneś używaćeval
).$var_value
jest odwrócenie cudzysłowu - zakładając bezpieczną wartość$var_name
(która może być tak samo niebezpiecznym założeniem, naprawdę) , wtedy powinieneś zawrzeć podwójne cudzysłowy w pojedynczych cudzysłowach - nie nawzajem.eval
, usingprintf
i jegobash
-specyficzny%q
. Nadal nie jest to zalecenieeval
, ale myślę, że jest bezpieczniejsze niż wcześniej. Fakt, że musisz poświęcić tyle wysiłku, aby to zadziałało, jest dowodem na to, że powinieneś używaćdeclare
lub nazwać referencje.set -- a bunch of args; eval "process2 $(process1 "$@")"
gdzieprocess1
drukuje tylko cytowane liczby"${1}" "${8}" "${138}"
. To szalone proste - i tak łatwe jak'"${'$((i=$i+1))'}" '
w większości przypadków. indeksowane referencje sprawiają, że jest bezpieczny, solidny i szybki . Nadal - głosowałem.Dobrym sposobem pracy
eval
jest zastąpienie goecho
testem.echo
ieval
działają tak samo (jeśli odłożymy na bok\x
rozszerzenie wykonane przez niektóreecho
implementacje, takie jakbash
pod pewnymi warunkami).Oba polecenia łączą argumenty z odstępem między nimi. Różnica polega na tym, że
echo
wyświetla wynik, podczas gdyeval
ocenia / interpretuje jako kod powłoki wynik.Tak więc, aby zobaczyć jaki kod powłoki
oceni, możesz uruchomić:
Nie tego chcesz, czego chcesz:
Również użycie
$(echo ...)
tutaj nie ma sensu.Aby wyświetlić powyższe, uruchomiłbyś:
Aby to zinterpretować, to po prostu:
Zauważ, że można go również użyć do ustawienia poszczególnych elementów tablicy:
Jak powiedzieli inni, jeśli nie zależy ci na tym
bash
, aby kod był konkretny, możesz użyćdeclare
jako:Należy jednak pamiętać, że ma pewne skutki uboczne.
Ogranicza zakres zmiennej do funkcji, w której jest uruchamiana. Nie można jej więc używać na przykład do:
Ponieważ zadeklarowałoby to
foo
lokalną zmienną,setvar
więc byłoby to bezużyteczne.bash-4.2
dodaliśmy-g
opcjędeclare
deklarowania zmiennej globalnej , ale nie tego też chcemy, ponieważsetvar
ustawilibyśmy zmienną globalną w przeciwieństwie do zmiennej wywołującej, gdyby funkcja wywołująca była jak w:co dałoby wynik:
Zauważ też, że chociaż
declare
jest wywoływanydeclare
(faktyczniebash
pożyczył koncepcję ztypeset
wbudowanej powłoki Korna ), jeśli zmienna jest już ustawiona,declare
nie deklaruje nowej zmiennej, a sposób wykonania przypisania zależy od typu zmiennej.Na przykład:
spowoduje inny wynik (i może mieć nieprzyjemne skutki uboczne), jeśli
varname
został wcześniej zadeklarowany jako skalar , tablica lub tablica asocjacyjna .źródło
bash
nie jest on dostępny (lub gdy zdasz sobie sprawę, że potrzebujesz lepszej / szybszej powłoki). Teeval
prace składniowe we wszystkich powłokach typu bourne i jest POSIX więc wszystkie systemy będą miećsh
gdzie to działa. (oznacza to również, że moja odpowiedź dotyczy wszystkich pocisków, a prędzej czy później, jak to często się zdarza, zobaczysz zamknięte pytanie bez duplikatu jako duplikat tego)$var_name
zawiera tokeny? ... jak;
?eval
ideclare
(myślećPATH
,TMOUT
,PS4
,SECONDS
...).export
. Nie podążam za nawiasami na końcu.Jeśli zrobisz:
... i
$name
zawiera jeden;
lub kilka innych tokenów, które powłoka może interpretować jako ograniczające proste polecenie - poprzedzone odpowiednią składnią powłoki, która zostanie wykonana.WYNIK
Czasami jednak możliwe jest rozdzielenie oceny i wykonania takich oświadczeń. Na przykład
alias
można użyć do wstępnej oceny polecenia. W poniższym przykładzie definicja zmiennej jest zapisywana w zmiennej,alias
którą można pomyślnie zadeklarować tylko wtedy, gdy$nm
zmienna, którą ocenia, nie zawiera bajtów, które nie pasują do znaków alfanumerycznych ASCII lub_
.eval
służy tutaj do obsługi wywoływania nowegoalias
z nazwy zmiennej. Ale wywoływana jest w ogóle tylko wtedy, gdy poprzedniaalias
definicja jest udana i chociaż wiem, że wiele różnych implementacji zaakceptuje wiele różnych rodzajówalias
nazw, nie spotkałem się jeszcze z jedną, która zaakceptuje całkowicie pustą .Definicja w tym
alias
jest_$nm
jednak, aby zapewnić, że żadne znaczące wartości środowiska nie zostaną zapisane. Nie znam żadnych wartych uwagi wartości środowiskowych rozpoczynających się od a,_
i jest to zwykle bezpieczny zakład na deklarację półprywatną.W każdym razie, jeśli
alias
definicja się powiedzie, zadeklaruje wartośćalias
nazwaną dla$nm
. Ieval
wywoła to tylkoalias
wtedy , gdy również nie zaczyna się od liczby - w przeciwnym razieeval
dostaje tylko zerowy argument. Więc jeśli oba warunki są spełnione,eval
wywołuje alias i tworzona jest definicja zmiennej zapisana w aliasie, po czym nowaalias
jest natychmiast usuwana z tabeli skrótów.źródło
;
nie jest dozwolone w nazwach zmiennych. Jeśli nie masz kontroli nad treścią$name
, musisz ją zdezynfekować również dlaexport
/declare
. Chociażexport
nie wykonuje kodu, ustawienie niektórych zmiennychPATH
,PS4
a wiele z nichinfo -f bash -n 'Bash Variables'
ma równie niebezpieczne skutki uboczne.eval
pierwszym przejściu - jest to rozszerzenie zmiennej. Jak powiedziałeś gdzie indziej, w tym kontekście jest to bardzo dozwolone. Mimo to argument $ PATH jest bardzo dobry - dokonałem niewielkiej edycji i dodam go później.zsh
,pdksh
,mksh
,yash
nie skarżą się naunset 'a;b'
.unset -v -- ...
.