Aktualizacja: Niektórzy mówią, że nie należy używać eval. Nie zgadzam się. Myślę, że ryzyko pojawia się, gdy można przekazać uszkodzone dane wejściowe eval. Jednak jest wiele typowych sytuacji, w których nie jest to ryzyko, dlatego warto wiedzieć, jak używać eval w każdym przypadku. Ta odpowiedź na temat przepełnienia stosów wyjaśnia ryzyko związane z eval i alternatywami dla eval. Ostatecznie to użytkownik decyduje, czy / kiedy eval jest bezpieczne i wydajne w użyciu.
Instrukcja bash evalumożliwia wykonanie wierszy kodu obliczonych lub uzyskanych przez skrypt bash.
Być może najprostszym przykładem byłby program bash, który otwiera inny skrypt basha jako plik tekstowy, odczytuje każdy wiersz tekstu i używa go evaldo wykonania po kolei. To zasadniczo to samo zachowanie co bashsource instrukcji , której można by użyć, chyba że byłoby konieczne wykonanie jakiejś transformacji (np. Filtrowania lub podstawiania) zawartości zaimportowanego skryptu.
Rzadko tego potrzebowałem eval, ale uważałem za przydatne czytanie lub zapisywanie zmiennych, których nazwy były zawarte w łańcuchach przypisanych do innych zmiennych. Na przykład, aby wykonywać akcje na zestawach zmiennych, zachowując mały ślad kodu i unikając redundancji.
evaljest koncepcyjnie prosta. Jednak ścisła składnia języka bash i kolejność analizowania interpretera bash mogą być zniuansowane i sprawiać, że evalwydają się tajemnicze i trudne w użyciu lub zrozumieniu. Oto najważniejsze informacje:
Argument przekazany do evaljest wyrażeniem tekstowym, które jest obliczane w czasie wykonywania. evalwykona końcowy przeanalizowany wynik swojego argumentu jako rzeczywistą linię kodu w skrypcie.
Składnia i kolejność analizowania są rygorystyczne. Jeśli wynik nie jest wykonywalną linią kodu bash, w zakresie twojego skryptu, program zawiesi się na evalinstrukcji, gdy spróbuje wykonać śmieci.
Podczas testowania możesz zamienić evalinstrukcję na echoi przyjrzeć się temu, co jest wyświetlane. Jeśli jest to prawidłowy kod w bieżącym kontekście, przepuszczenie go evalbędzie działać.
Poniższe przykłady mogą pomóc wyjaśnić, jak działa eval ...
Przykład 1:
eval instrukcja przed „normalnym” kodem to NOP
$ eval a=b
$ eval echo $a
b
W powyższym przykładzie pierwsze evalstwierdzenia są bezcelowe i można je wyeliminować. evalnie ma sensu w pierwszej linii, ponieważ nie ma aspektu dynamicznego w kodzie, tj. został już przeanalizowany do końcowych wierszy kodu basha, więc byłby identyczny z normalną instrukcją kodu w skrypcie bash. Drugi również evaljest bezcelowy, ponieważ chociaż istnieje etap przetwarzania konwertujący $ana jego odpowiednik w postaci dosłownego ciągu, nie ma żadnego pośredniego kierunku (np. Brak odniesienia przez wartość ciągu rzeczywistego rzeczownika bash lub zmiennej skryptu trzymanej przez bash), więc zachowałby się identycznie jako wiersz kodu bez evalprefiksu.
Przykład 2:
Wykonaj przypisanie zmiennej, używając nazw zmiennych przekazanych jako wartości ciągów.
$ key="mykey"
$ val="myval"
$ eval $key=$val
$ echo $mykey
myval
Gdyby tak było echo $key=$val, wynik wyglądałby tak:
mykey=myval
To , będąc końcowym wynikiem analizy ciągu znaków, zostanie wykonane przez eval, stąd wynik instrukcji echo na końcu ...
Przykład 3:
Dodanie więcej pośrednictwa do przykładu 2
$ keyA="keyB"
$ valA="valB"
$ keyB="that"
$ valB="amazing"
$ eval eval \$$keyA=\$$valA
$ echo $that
amazing
Powyższe jest nieco bardziej skomplikowane niż w poprzednim przykładzie, w większym stopniu polegając na kolejności parsowania i osobliwościach basha. evalLinia byłaby grubsza się wewnętrznie przetwarzane w następującej kolejności (zwrócić uwagę na następujące stwierdzenia są pseudokod, nie prawdziwy kod, tylko próba pokazania jak stwierdzenie byłoby uzyskać w podziale na etapy wewnętrznie dojść do wyniku końcowego) .
eval eval \$$keyA=\$$valA # substitution of $keyA and $valA by interpreter
eval eval \$keyB=\$valB # convert '$' + name-strings to real vars by eval
eval $keyB=$valB # substitution of $keyB and $valB by interpreter
eval that=amazing # execute string literal 'that=amazing' by eval
Jeśli zakładana kolejność analizowania nie wyjaśnia, jakie działania eval wystarczają, trzeci przykład może bardziej szczegółowo opisywać parsowanie, aby pomóc wyjaśnić, co się dzieje.
Przykład 4:
Odkryj, czy zmienne, których nazwy są zawarte w łańcuchach, same zawierają wartości łańcuchowe.
a="User-provided"
b="Another user-provided optional value"
c=""
myvarname_a="a"
myvarname_b="b"
myvarname_c="c"
for varname in "myvarname_a" "myvarname_b" "myvarname_c"; do
eval varval=\$$varname
if [ -z "$varval" ]; then
read -p "$varname? " $varname
fi
done
W pierwszej iteracji:
varname="myvarname_a"
Bash analizuje argument evali evalwidzi to dosłownie w czasie wykonywania:
eval varval=\$$myvarname_a
Poniższy pseudokod próbuje zilustrować, jak bash interpretuje powyższą linię rzeczywistego kodu, aby uzyskać końcową wartość wykonywaną przez eval. (następujące wiersze opisowe, a nie dokładny kod bash):
1. eval varval="\$" + "$varname" # This substitution resolved in eval statement
2. .................. "$myvarname_a" # $myvarname_a previously resolved by for-loop
3. .................. "a" # ... to this value
4. eval "varval=$a" # This requires one more parsing step
5. eval varval="User-provided" # Final result of parsing (eval executes this)
Po zakończeniu całego parsowania wynik jest wykonywany, a jego efekt jest oczywisty, co pokazuje, że nie ma w sobie nic szczególnie tajemniczego eval, a złożoność polega na analizie jego argumentu.
varval="User-provided"
Pozostały kod w powyższym przykładzie po prostu sprawdza, czy wartość przypisana do zmiennej $ varval jest null, a jeśli tak, monituje użytkownika o podanie wartości.
$($n)działa$nw podpowłoce. Próbuje uruchomić polecenie,1które nie istnieje.