Co to jest polecenie „eval” w bash?

176

Co możesz zrobić z evalpoleceniem? Dlaczego to jest przydatne? Czy to jakaś wbudowana funkcja w bash? Nie ma manna to strony.

LanceBaynes
źródło
28
Użyj, type commandaby dowiedzieć się, jakiego typu jest polecenie . ( type evalw tym przypadku)
rozcietrzewiacz
9
„eval jest wbudowaną powłoką”
Nuno Rafael Figueiredo,
3
help evalpobrać ze swojej powłoki stronę „man”
m-ric
eval jest wbudowanym programem bash i jest udokumentowany na stronie podręcznika bash. Więc po prostu wpisz „man bash” i wyszukaj odpowiednią sekcję eval. Dotyczy to także innych wbudowanych bashów.
Dirk Thannhäuser,

Odpowiedzi:

132

evaljest częścią POSIX. Jest to interfejs, który może być wbudowany w powłokę.

Jest to opisane w „Podręczniku programisty POSIX”: http://www.unix.com/man-page/posix/1posix/eval/

eval - construct command by concatenating arguments

Weźmie argument i zbuduje jego polecenie, które zostanie wykonane przez powłokę. Oto przykład strony man:

1) foo=10 x=foo
2) y='$'$x
3) echo $y
4) $foo
5) eval y='$'$x
6) echo $y
7) 10
  1. W pierwszym wierszu definiujesz $fooza pomocą wartości '10'i $xwartości 'foo'.
  2. Teraz zdefiniuj $y, który składa się z ciągu '$foo'. Znak dolara musi być poprzedzony znakiem ucieczki '$'.
  3. Aby sprawdzić wynik, echo $y.
  4. Wynik będzie ciągiem '$foo'
  5. Teraz powtarzamy zadanie z eval. Najpierw oceni $xdo łańcucha 'foo'. Teraz mamy oświadczenie, y=$fooktóre zostanie ocenione y=10.
  6. Wynikiem echo $yjest teraz wartość '10'.

Jest to powszechna funkcja w wielu językach, np. Perl i JavaScript. Więcej przykładów na perldoc eval: http://perldoc.perl.org/functions/eval.html

echox
źródło
4
W terminologii powłoki evaljest wbudowana, a nie funkcja. W praktyce wbudowane zachowują się bardzo podobnie do funkcji, które nie mają definicji w języku, ale nie do końca (co staje się oczywiste, jeśli jesteś wystarczająco pokręcony, aby zdefiniować funkcję o nazwie eval).
Gilles
dzięki, naprawiłem to :-)
echox
4
Jaka jest różnica między eval a backtickami?
JohnyTex,
5
Backticks są skrótem do wykonywania kolejnej całej powłoki, co oznacza, że ​​będziesz mieć inny potomny proces bash / sh w twoim skrypcie.
Aaron R.,
Dlaczego echo $ y (etap 3) zwraca foo (10) zamiast $ foo? (tj. tylko wartość foo zamiast ciągu $ + wartość foo)?
JohnDoea,
60

Tak, evaljest wewnętrznym poleceniem bash, więc jest opisane na bashstronie man.

eval [arg ...]
    The  args  are read and concatenated together into a single com-
    mand.  This command is then read and executed by the shell,  and
    its  exit status is returned as the value of eval.  If there are
    no args, or only null arguments, eval returns 0.

Zwykle stosuje się go w połączeniu z zastępowaniem poleceń . Bez wyraźnej evalpowłoki powłoka próbuje wykonać wynik podstawienia polecenia, a nie go ocenić .

Powiedz, że chcesz zakodować odpowiednik VAR=value; echo $VAR. Zwróć uwagę na różnicę w sposobie, w jaki powłoka obsługuje pisma echo VAR=value:

  1. andcoz@...:~> $( echo VAR=value )
    bash: VAR=value: command not found
    andcoz@...:~> echo $VAR
    <empty line>

    Powłoka próbuje wykonać echoi VAR=valuejako dwóch oddzielnych komend. Zgłasza błąd dotyczący drugiego ciągu. Zadanie pozostaje nieskuteczne.

  2. andcoz@...:~> eval $( echo VAR=value )
    andcoz@...:~> echo $VAR
    value
    Powłoka scala (łączy) dwa ciągi echoi VAR=valueanalizuje tę pojedynczą jednostkę zgodnie z odpowiednimi regułami i wykonuje ją.

Last but not least, evalmoże być bardzo niebezpiecznym poleceniem. Wszelkie dane wejściowe do evalpolecenia muszą być dokładnie sprawdzane, aby uniknąć problemów z bezpieczeństwem.

andcoz
źródło
18

evalnie ma strony podręcznika, ponieważ nie jest to osobne polecenie zewnętrzne, ale wbudowana powłoka, co oznacza polecenie wewnętrzne i znane tylko powłoce ( bash). Odpowiednia część strony podręcznika bashmówi:

eval [arg ...]
    The args are read and concatenated together into a single command.  
    This command is then  read  and executed by the shell, and its exit 
    status is returned as the value of eval.  If there are no args, or only 
    null arguments, eval returns 0

Ponadto dane wyjściowe, jeśli help evaljest:

eval: eval [arg ...]
    Execute arguments as a shell command.

    Combine ARGs into a single string, use the result as input to the shell,
    and execute the resulting commands.

    Exit Status:
    Returns exit status of command or success if command is null.

evaljest potężnym poleceniem i jeśli zamierzasz go używać, powinieneś bardzo ostrożnie unikać potencjalnych zagrożeń bezpieczeństwa, które się z tym wiążą.

jw013
źródło
16

Instrukcja eval mówi powłoce, aby przyjęła argumenty eval jako polecenie i uruchomiła je z wiersza poleceń. Przydaje się w sytuacji takiej jak poniżej:

W swoim skrypcie, jeśli definiujesz polecenie w zmiennej, a później chcesz użyć tego polecenia, powinieneś użyć eval:

/home/user1 > a="ls | more"
/home/user1 > $a
bash: command not found: ls | more
/home/user1 > # Above command didn't work as ls tried to list file with name pipe (|) and more. But these files are not there
/home/user1 > eval $a
file.txt
mailids
remote_cmd.sh
sample.txt
tmp
/home/user1 >
Nikhil Gupta
źródło
3
Komentarz w wierszu 4 twojego przykładu jest niepoprawny: Powinien brzmieć: „Powyższe polecenie nie działało, ponieważ bash próbował znaleźć polecenie o nazwie ls | more. Innymi słowy: Nazwa pojedynczego polecenia składała się z dziewięciu znaków, w tym spacji i symbolu potoku .
SPI
Mam dokładnie takie samo zachowanie, jakie zgłosił. bash --version == GNU bash, wersja 3.2.57 (1) -release (x86_64-apple-darwin18)
Alexander Bird
10

Co to jest eval?

eval to polecenie powłoki, które zwykle jest implementowane jako wbudowane.

W POSIX jest wymieniony jako część „2.14. Specjalne narzędzia wbudowane” w pozycji „eval” .
Wbudowane oznacza:

Termin „wbudowany” oznacza, że ​​powłoka może bezpośrednio uruchomić narzędzie i nie musi go szukać.

Co to robi?

Mówiąc najprościej: sprawia, że ​​wiersz wejściowy jest analizowany dwukrotnie .

Jak to robi?

Powłoka ma sekwencję kroków, które wykonuje, aby „przetworzyć” linię. Możesz spojrzeć na ten obraz i zdać sobie sprawę, że eval jest jedyną linią, która idzie w górę, z powrotem do kroku 1 po lewej stronie. Z opisu POSIX :

2.1 Wprowadzenie do powłoki

  1. Powłoka odczytuje dane wejściowe ....
  2. Powłoka dzieli dane wejściowe na tokeny: słowa i operatory
  3. Powłoka analizuje dane wejściowe na polecenia proste i złożone.
  4. Powłoka wykonuje różne rozszerzenia (osobno) ...
  5. Powłoka dokonuje przekierowania i usuwa operatory przekierowania oraz ich operandy z listy parametrów.
  6. Powłoka wykonuje funkcję, wbudowany plik wykonywalny lub skrypt ...
  7. Opcjonalnie powłoka czeka na zakończenie komendy i pobiera status wyjścia.

W kroku 6 zostanie wykonane wbudowane.
W kroku 6 eval powoduje, że przetworzona linia jest odsyłana z powrotem do kroku 1.
Jest to jedyny warunek, w którym sekwencja wykonania wraca.

Dlatego mówię: w eval linia wejściowa jest analizowana dwukrotnie .

Efekty parsowania dwukrotnie.

Pierwszy.

I najważniejszy efekt do zrozumienia. Jest to, że jedną z konsekwencji po raz pierwszy linia podlega siedmiu powłoki kroków przedstawionych powyżej, jest cytowanie . W kroku 4 (rozwinięcia) znajduje się również sekwencja kroków umożliwiająca wykonanie wszystkich rozszerzeń , z których ostatnim jest usunięcie oferty :

Usunięcie wyceny należy zawsze wykonać na końcu.

Tak więc zawsze usuwany jest jeden poziom cytowania.

Druga.

W wyniku tego pierwszego efektu dodatkowe / różne części linii są narażone na parsowanie powłoki i wszystkie pozostałe kroki.

Przykład.

Pośrednictwo

Pozwala to na wykonywanie rozszerzeń pośrednich:

a=b b=c    ;    eval echo \$$a            ### shall produce "c"

Dlaczego? Ponieważ w pierwszej pętli $cytowany jest pierwszy .
Jako taki jest ignorowany dla rozszerzeń przez powłokę.
Następny $z nazwą a jest rozwinięty, aby uzyskać „b”.
Następnie jeden poziom cytowania jest usuwany, co powoduje, że pierwszy $nie jest cytowany .
Koniec pierwszej pętli.

To w drugiej pętli łańcuch $bjest odczytywany przez powłokę.
Następnie rozwinięty do „c”
I podany jako argument do echo.

Aby „zobaczyć”, co eval wytworzy w pierwszej pętli (do ponownej oceny), użyj echa. Lub dowolne polecenie / skrypt / program, który wyraźnie pokazuje argumenty:

$ a=b b=c
$ eval echo \$$a;
c

Zastąp eval przez echo, aby „zobaczyć”, co się dzieje:

$ echo echo \$$a
echo $b

Możliwe jest również pokazanie wszystkich „części” linii za pomocą:

$ printf '<%s> ' echo \$$a
<echo> <$b>

Który w tym przykładzie jest tylko jednym echem i jedną zmienną, ale pamiętaj o tym, aby pomóc w ocenie bardziej złożonych przypadków.

Korekta.

Trzeba powiedzieć, że: w powyższym kodzie jest błąd, widzisz go ?.
Łatwo: brakuje niektórych cytatów.

W jaki sposób? możesz zapytać. Proste, zmieńmy zmienne (nie kod):

$ a=b b="hi     jk"
$ eval echo \$$a
hi jk

Widzisz brakujące miejsca?
Jest tak, ponieważ wartość wewnątrz $bzostała podzielona przez powłokę.

Jeśli to Cię nie przekonuje, spróbuj tego:

$ a=b b="hi  *  jk"
$ eval echo \$$a              ### warning this will expand to the list  
                              ### of all files in the present directory.

Dlaczego?

Brakujące cytaty. Aby działał poprawnie (dodaj cytaty wewnętrzne "$a"i zewnętrzne \").
Spróbuj tego (jest całkowicie bezpieczny):

$ a=b b="hi      *       jk"
$ eval echo \" \$"$a" \"
hi      *       jk

O instrukcji:

Nie ma strony man dla tego ..

Nie, nie ma na to niezależnej strony podręcznika. Wyszukiwanie instrukcji z man -f evallub nawet apropos evalnie pokazuje żadnego wpisu.

Jest zawarty w środku man bash. Jak każde wbudowane.
Wyszukaj „SHELL BUILTIN COMMANDS”, a następnie „eval”.

Łatwiejszym sposobem na uzyskanie pomocy jest: w bash, możesz zrobić, help evalaby zobaczyć pomoc dla wbudowanego.

Dlaczego eval nazywa się złem?

Ponieważ dynamicznie wiąże tekst z kodem.

Innymi słowy: konwertuje listę swoich argumentów (i / lub rozwinięcie takich argumentów) na wykonywaną linię. Jeśli z jakiegokolwiek powodu osoba atakująca ustali argument, wykonasz kod osoby atakującej.

Lub nawet prościej, z eval mówisz każdemu, kto zdefiniował wartość jednego lub kilku argumentów:

No dalej, usiądź tutaj i wpisz dowolną linię poleceń, wykonam to z moimi mocami.

Czy to niebezpieczne? Powinno być jasne dla wszystkich, że tak jest.

Zasada bezpieczeństwa dla eval powinny być:
Wykonuje tylko eval na zmiennych, do których ty dały jego wartość.

Przeczytaj więcej szczegółów tutaj .


źródło
10

evaljest cechą najbardziej interpretowanych językach ( TCL, python, ruby...), nie tylko muszli. Służy do dynamicznej oceny kodu.

W powłokach jest zaimplementowany jako wbudowane polecenie powłoki.

Zasadniczo evalbierze ciąg znaków jako argument i ocenia / interpretuje w nim kod. W powłokach evalmoże przyjąć więcej niż jeden argument, ale evalpo prostu łączy je w celu utworzenia ciągu do oceny.

Jest bardzo wydajny, ponieważ można dynamicznie konstruować kod i uruchamiać go, czego nie można zrobić w skompilowanych językach, takich jak C.

Lubić:

varname=$1 varvalue=$2
eval "$varname=\$varvalue" # evaluate a string like "foo=$varvalue"
                           # which in Bourne-like shell language
                           # is a variable assignment.

Jest to również niebezpieczne, ponieważ ważne jest odkażanie dynamicznych (dostarczonych zewnętrznie) części tego, co jest przekazywane, z evaltego samego powodu, że jest interpretowane jako kod powłoki.

Na przykład, powyżej, jeśli $1jest evil-command; var, evalkończy ocenę evil-command; var=$varvaluekodu powłoki, a następnie uruchamia ją evil-command.

Zło evalczęsto jest przesadzone.

OK, to niebezpieczne, ale przynajmniej wiemy, że jest niebezpieczne.

Wiele innych poleceń oceni Shellcode w swoich argumentów, jeśli nie oczyszczone, jak (w zależności od powłoki), [aka test, export, printf, GNU sed, awki oczywiście sh/ bash/ perli wszyscy tłumacze ...

Przykłady (tutaj przy użyciu unamejako evil-commandi $ajako dane zewnętrzne niezarządzane):

$ a='$(uname>&2)' sh -c 'eval "echo $a"'
Linux

$ a='x[0$(uname>&2)]' mksh -c 'export "$a=$b"'
Linux
$ a='x[0$(uname>&2)]' ksh93 -c 'printf "%d\n" "$a"'
Linux
0
$ a='x[0$(uname>&2)]' ksh93 -c '[ "$a" -gt 0 ]'
Linux
$ a=$'bar/g;e uname>&2\n;s//'; echo foo | sed "s/foo/$a/g"
Linux
bar
$ a='";system("uname");"'; awk "BEGIN{print \"$a\"}"

Linux
$ a=';uname'; sh -c "echo $a"

Linux

Ci sed, export... Polecenia mogą być uważane za bardziej niebezpieczne, ponieważ podczas, gdy jest oczywiste, eval "$var"spowoduje, że zawartość $varbyć oceniane jako Shellcode, to nie jest tak oczywiste, ze sed "s/foo/$var/"albo export "$var=value"albo [ "$var" -gt 0 ]. Niebezpieczeństwo jest takie samo, ale ukryte jest w innych poleceniach.

Stéphane Chazelas
źródło
@BinaryZebra, sedpodobnie jak evalprzekazywanie łańcucha zawartego w zmiennej, i dla obu, treść tego łańcucha jest ostatecznie oceniana jako kod powłoki, więc sedjest tak niebezpieczne, jak evalto mówię. sedjest przekazywany ciąg zawierający uname(uname nie został jeszcze wykonany) i przez wywołanie sedpolecenia uname kończy się wykonywanie. Jak na eval. W sed 's/foo/$a/g'nie przekazujesz niezaszyfrowanych danych sed, nie o tym tutaj mówimy.
Stéphane Chazelas,
2

Ten przykład może rzucić nieco światła:

#!/bin/bash
VAR1=25
VAR2='$VAR1'
VAR3='$VAR2'
echo "$VAR3"
eval echo "$VAR3"
eval eval echo "$VAR3"

Wyjście powyższego skryptu:

$VAR2
$VAR1
25
LoMaPh
źródło
Dziękujemy za Twój wkład, ale choć są tak zabawne, że tak naprawdę nie jesteśmy zainteresowani opracowaniem listy (nieco innych) przykładów użycia eval. Czy uważasz, że istnieje jakiś fundamentalny, krytyczny aspekt jego funkcjonalności, evalktóry nie został uwzględniony w istniejących odpowiedziach? Jeśli tak, wyjaśnij to i użyj przykładu, aby zilustrować swoje wyjaśnienie. Proszę nie odpowiadać w komentarzach; edytuj  swoją odpowiedź, aby była jaśniejsza i bardziej kompletna.
Scott,