2012-03-30 26 views
22

Wydaje się, że zalecany sposób prowadzenia pośrednie ustawienie zmiennej w bashu jest użycie eval:Pośrednie przypisanie zmiennej w bashu

var=x; val=foo 
eval $var=$val 
echo $x # --> foo 

Problemem jest zwykle jednym z eval:

var=x; val=1$'\n'pwd 
eval $var=$val # bad output here 

(i ponieważ jest to zalecane w wielu miejscach, zastanawiam się, ile skryptów jest podatnych na ten ...). g (uciekł) cytaty naprawdę nie działa:

var=x; val=1\"$'\n'pwd\" 
eval $var=\"$val\" # fail with the above 

Chodzi o to, że bash został pośrednie odniesienie zmienna pieczone w (z ${!foo}), ale nie widzę żadnej takiej drogę do zrobienia zadanie pośredni - jest Czy istnieje jakiś rozsądny sposób, aby to zrobić?

Dla przypomnienia, znalazłem rozwiązanie, ale to nie jest coś, co Pomyślę „sane” ...:

eval "$var='"${val//\'/\'\"\'\"\'}"'" 

Odpowiedz

2

Głównym punktem jest to zalecany sposób to zrobić:

eval "$var=\$val" 

z RHS również pośrednio. Ponieważ eval jest używane w tym samym środowisku , będzie miało $val powiązane, więc odroczenie go działa, a od jest teraz tylko zmienną. Ponieważ zmienna $val ma znane nazwisko, nie ma żadnych problemów z cytowania, a to mogło nawet zapisać jako:

eval $var=\$val 

Ale ponieważ lepiej jest zawsze dodać cytaty, ta pierwsza jest lepsza, lub nawet to:

eval "$var=\"\$val\"" 

lepszą alternatywą w bash, która została wymieniona cała rzecz, że unika eval minimum (i nie jest tak subtelny jak declare itp):

printf -v "$var" "%s" "$val" 

Choć nie jest to bezpośrednia odpowiedź, co pierwotnie zapytał ...

13
eval "$var=\$val" 

Argument eval zawsze powinny być pojedynczym ciąg znaków ujęty w pojedyncze lub podwójne cudzysłowy. Cały kod odbiegający od tego wzoru ma pewne niezamierzone zachowanie w przypadkach skrajnych, takich jak nazwy plików ze znakami specjalnymi.

Po rozszerzeniu argumentu o eval przez powłokę, $var zostaje zastąpiony nazwą zmiennej, a \$ zostaje zastąpiony prostym dolarem. Ciąg, który jest oceniany, staje się zatem:

varname=$value 

To jest dokładnie to, co chcesz.

Zazwyczaj wszystkie wyrażenia formularza $varname powinny być ujęte w cudzysłowy. Istnieją tylko dwa miejsca, w których można pominąć cytaty: przypisania zmiennych i case. Ponieważ jest to przypisanie zmiennej, cytaty nie są tutaj potrzebne. Nie boli, chociaż, tak można też pisać oryginalnego kodu jako:

eval "$var=\"the value is $val\"" 
+0

wskazanie pośrednie na RHS nie jest to, czego szukam. –

+1

(* czoło-klaps *) Bah, całkowicie przegapiłem, dlaczego * nie * chcę, aby w kierunku RHS. Ponieważ twoja odpowiedź w ogóle o tym nie mówi, będę ją teraz edytować, zamiast odpowiadać ... samemu sobie patrzę ... –

+0

Fantastycznie! W przeszłości robiłem trochę skomplikowanych nonsensów "eval eval export". Bardzo dziękuję. W przypadku pracowników Google przejdź do powyższej odpowiedzi, a nie do formatu eksportu eval eval. – bgStack15

24

nieco lepszy sposób, unikając możliwych wpływ na bezpieczeństwo korzystania eval, to

declare $var="$val" 

Zauważ, że declare jest synonimem dla typeset w bash. Komenda typeset jest szerzej obsługiwane (ksh i zsh również użyć go):

typeset $var="$val" 
+0

To nie wygląda na przenośne dla pocisków mniejszych niż bash – MarcH

+0

Rzeczywiście; podczas gdy 'declare' jest rozszerzeniem standardu POSIX, to jest również synonimem' typeset', który * jest * obsługiwany przez inne główne powłoki ('ksh' i' zsh', a mianowicie). Pociski, które nie obsługują czegoś podobnego, muszą ostrożnie używać 'eval'. – chepner

+3

'eval" $ var = '$ val' "' nie jest na tyle ostrożny: jeśli zawartość zawiera literalne pojedyncze cudzysłowy, mogą łatwo uciec. –

14

Bash ma rozszerzenie do printf że oszczędza swój wynik do zmiennej:

printf -v "${VARNAME}" '%s' "${VALUE}" 

To uniemożliwia wszelkie możliwe problemy uciekają .

Jeśli używasz nieprawidłowy identyfikator $VARNAME komenda zawiedzie i powrót kod stanu 2:

$ printf -v ';;;' foobar; echo $? 
bash: printf: `;;;': not a valid identifier 
2 
Powiązane problemy