Що таке команда «eval» в баші?


176

Що ви можете зробити з evalкомандою? Чому це корисно? Це якась вбудована функція в bash? Немає manсторінки для цього ..


28
Використовуйте type commandдля того, щоб дізнатися про тип команди . ( type evalу цьому випадку)
rozcietrzewiacz

9
"eval - це вбудована оболонка"
Nuno Rafael Figueiredo

3
help evalщоб отримати сторінку "man" з вашої оболонки
m-ric

eval - це вбудований в bash і задокументований на man-сторінці сторінки bash. Тому просто введіть "man bash" і знайдіть відповідний розділ для eval. Це стосується і інших вбудованих башів.
Дірк Тханхейзер

Відповіді:


132

evalє частиною POSIX. Його інтерфейс, який може бути вбудованою оболонкою.

Її описано у "Посібнику програміста POSIX": http://www.unix.com/man-page/posix/1posix/eval/

eval - construct command by concatenating arguments

Він візьме аргумент і побудує команду з нього, яка буде виконана оболонкою. Ось приклад вручну:

1) foo=10 x=foo
2) y='$'$x
3) echo $y
4) $foo
5) eval y='$'$x
6) echo $y
7) 10
  1. У першому рядку ви визначаєте $fooзі значенням '10'і $xзі значенням 'foo'.
  2. Тепер визначимо $y, що складається з рядка '$foo'. Знак долара потрібно уникати '$'.
  3. Щоб перевірити результат, echo $y.
  4. Результатом буде рядок '$foo'
  5. Тепер повторюємо завдання з eval. Спочатку вона буде оцінена $xдо рядка 'foo'. Тепер у нас є заява, на y=$fooяку буде отримано оцінку y=10.
  6. Результатом echo $yтепер є значення '10'.

Це загальна функція у багатьох мовах, наприклад, Perl та JavaScript. Подивіться на perldoc eval, щоб отримати додаткові приклади: http://perldoc.perl.org/functions/eval.html


4
У оболонці термінологія eval- це вбудована, а не функція. На практиці вбудовані модулі ведуть себе так само, як і функції, які не мають мовного визначення, але не зовсім (як це стає очевидним, якщо ви скручені досить, щоб визначити функцію, що називається eval).
Жиль

thx, виправлено, що :-)
відлуння

4
Яка різниця між eval і backtikks `?
JohnyTex

5
Повернення - це скорочення для виконання ще однієї цілої оболонки, тобто у вас буде ще один дочірній процес bash / sh, який працює у вашому сценарії.
Аарон Р.

Чому echo $ y (етап 3) повертає foo (10) замість $ foo? (тобто просто значення foo замість рядка $ + значення foo)?
JohnDoea

60

Так, evalце внутрішня команда bash, тому вона описана на bashсторінці 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.

Зазвичай він використовується в поєднанні з командною заміною . Без явного evalоболонки намагається виконати результат заміни команди, а не оцінювати його.

Скажіть, що ви хочете кодувати еквівалент VAR=value; echo $VAR. Зверніть увагу на різницю в тому, як оболонка обробляє записи echo VAR=value:

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

    Оболонка намагається виконати echoі VAR=valueяк дві окремі команди. Він видає помилку щодо другого рядка. Призначення залишається неефективним.

  2. andcoz@...:~> eval $( echo VAR=value )
    andcoz@...:~> echo $VAR
    value
    Оболонка об'єднує (об'єднує) два рядки echoі VAR=valueаналізує цей єдиний блок відповідно до відповідних правил і виконує його.

І останнє, але не менш важливе, evalможе бути дуже небезпечною командою. Будь-який вхід до evalкоманди повинен бути ретельно перевірений, щоб уникнути проблем із безпекою.


18

evalне має довідної сторінки, оскільки це не окрема зовнішня команда, а скоріше вбудована оболонка, що означає внутрішню команду та відома лише оболонкою ( bash). Відповідна частина bashдовідкової сторінки говорить:

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

Крім того, вихід, якщо help eval:

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.

evalце потужна команда, і якщо ви збираєтесь її використовувати, ви повинні бути дуже обережними, щоб запобігти можливим ризикам безпеки, які пов'язані з нею.


16

Оператор eval сповіщає оболонку приймати аргументи eval як команду та запускати їх через командний рядок. Це корисно в такій ситуації, як нижче:

У вашому сценарії, якщо ви визначаєте команду в змінну, і пізніше ви хочете використовувати цю команду, тоді ви повинні використовувати 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 >

3
Коментар у рядку 4 вашого прикладу невірний: він повинен бути "Над командою не спрацювало, тому що Баш намагався знайти команду, що називається ls | more. Іншими словами: ім'я однієї команди складалося з дев'яти символів, включаючи пробіли та символ труби .
cfi

Я отримую саме таку поведінку, про яку він повідомляв. bash --version == GNU bash, версія 3.2.57 (1) -випуск (x86_64-apple-darwin18)
Олександр Птах

10

Що таке eval?

eval - команда оболонки, яка зазвичай реалізується як вбудована.

У POSIX він вказаний як частина "2.14. Спеціальні вбудовані утиліти" на вході "eval" .
Що вбудовано означає:

Термін "вбудований" означає, що оболонка може виконувати утиліту безпосередньо, і не потрібно її шукати.

Що це робить?

Простіше кажучи: робить вхідний рядок для розбору два рази .

Як це робити?

Оболонка має послідовність кроків, за якими слід «обробити» рядок. Ви можете подивитися на це зображення і зрозуміти, що eval - це єдина лінія, яка йде вгору, назад до кроку 1, зліва. З опису POSIX :

2.1 Введення в оболонку

  1. Оболонка зчитує своє введення ....
  2. Оболонка розбиває введення в лексеми: слова та оператори
  3. Оболонка аналізує вхід на прості та складені команди.
  4. Оболонка виконує різні розширення (окремо) ...
  5. Оболонка виконує перенаправлення та видаляє операторів перенаправлення та їх операндів зі списку параметрів.
  6. Оболонка виконує функцію, вбудований, виконуваний файл або сценарій ...
  7. Оболонка необов'язково чекає, коли команда завершить, і збирає статус виходу.

На етапі 6 буде виконано вбудований модуль.
На етапі 6 eval викликає повернення оброблюваної лінії до етапу 1.
Це єдина умова, при якій послідовність виконання повертається назад.

Ось чому я кажу: З eval вхідний рядок аналізується двічі .

Ефекти розбору двічі.

Перший.

І найголовніший ефект, який потрібно зрозуміти. Чи є це одним із наслідків першого разу, коли рядок піддається семи етапам оболонки, показаним вище, цитуванням . Всередині кроку 4 (розширення) також є послідовність кроків для виконання всіх розширень , останній з яких - Видалення цитат :

Видалення котирувань завжди повинно виконуватися останнім.

Отже, завжди є один рівень котирування.

Друге.

Як наслідок цього першого ефекту, додаткові / різні частини лінії піддаються розбору оболонки та всіх інших кроків.

Приклад.

Непрямий.

Це дозволяє виконувати непрямі розширення:

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

Чому? Тому що на першому циклі $цитується перший .
Таким чином, оболонка ігнорується для розширень.
Наступний $з назвою a розгорнуто для отримання "b".
Потім один рівень котирування видаляється, роблячи перший без $котирування.
Кінець першої петлі.

Саме тоді на другому циклі рядок $bзчитується оболонкою.
Потім розширено на "с"
і подано як аргумент до echo.

Щоб "побачити", що eval створить на першому циклі (оцінюється ще раз), використовуйте ехо. Або будь-яка команда / сценарій / програма, яка чітко показує аргументи:

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

Замініть eval на відлуння, щоб "побачити", що відбувається:

$ echo echo \$$a
echo $b

Також можна показати всі "частини" рядка за допомогою:

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

Що в цьому прикладі є лише одним відлунням і однією змінною, але пам’ятайте це, щоб допомогти в оцінці більш складних випадків.

Корекція.

Потрібно сказати, що: у коді є помилка, ви бачите її?
Легко: деякі цитати відсутні.

Як? ви можете запитати. Просто, давайте змінимо змінні (не код):

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

Бачите пропущені пробіли?
Це тому, що значення всередині $bбуло розділене оболонкою.

Якщо це не переконує вас, спробуйте:

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

Чому?

Пропущені цитати. Щоб вона працювала правильно (додайте внутрішні "$a"та зовнішні \"лапки).
Спробуйте це (цілком безпечно):

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

Про посібник:

Немає чоловічої сторінки для цього ..

Ні, для цього не існує незалежної чоловічої сторінки. Пошук посібника з записом man -f evalабо навіть apropos evalне показує жодного запису.

Він входить всередину man bash. Як і будь-який вбудований.
Шукайте "ЗРОБИТИ БУЛІТИННІ КОМАНДИ", а потім - "Eval".

Найпростіший спосіб отримати допомогу: У bash, ви можете зробити, help evalщоб побачити допомогу для вбудованої.

Чому eval називають злом?

Тому що це обов'язковий текст для кодування динамічно.

Іншими словами: він перетворює список своїх аргументів (та / або розширення таких аргументів) у виконаний рядок. Якщо з будь-якої причини зловмисник встановив аргумент, ви будете виконувати код зловмисника.

Або ще простіше, з eval ви говорите, хто визначив значення одного або декількох аргументів:

Ходімо, сидіть тут і введіть будь-який командний рядок, я виконуватиму його своїми силами.

Це небезпечно? Всім повинно бути зрозуміло, що це так.

Правилом безпеки для eval повинно бути:
Виконати eval лише на змінних, яким ви вказали його значення.

Детальніше читайте тут .


10

evalє рисою більшості різних мов ( TCL, python, ruby...), а не тільки снаряди. Він використовується для динамічної оцінки коду.

У оболонках вона реалізована як команда, побудована в оболонці.

В основному, evalприймає рядок як аргумент і оцінює / інтерпретує код в ньому. У оболонках evalможна брати більше одного аргументу, але evalпросто об'єднує ті, щоб сформувати рядок для оцінки.

Це дуже потужно, тому що ви можете динамічно конструювати код і запускати його, що ви не можете зробити на компільованих мовах, таких як C.

Подібно до:

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

Але це також небезпечно, оскільки важливо очистити динамічні (надані зовні) частини того, що передається, саме evalз тієї причини, що це інтерпретується як код оболонки.

Наприклад, вище, якщо $1є evil-command; var, evalв кінцевому підсумку буде evil-command; var=$varvalueоцінено код оболонки, а потім виконати це evil-command.

Зло evalчасто є перебільшеним.

Гаразд, це небезпечно, але принаймні ми знаємо, що це небезпечно.

Багато інших команд буде оцінювати код оболонки в його аргументи , якщо не продезінфікувати, наприклад , ( в залежності від оболонки), [він же test, export, printf, GNU sed, awkі, звичайно , sh/ bash/ perlі все перекладачі ...

Приклади (тут використовують unameяк несекретизовані дані як evil-commandі $aяк надані зовні):

$ 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

Ці sed, export... команди можна вважати більш небезпечними, оскільки, мабуть, це очевидно eval "$var", що вміст $varоцінюється як код оболонки, це не так очевидно з sed "s/foo/$var/"або export "$var=value"або [ "$var" -gt 0 ]. Небезпечність однакова, але вона прихована в інших командах.


@BinaryZebra, sedяк , наприклад eval, передається рядок, що міститься в змінній, і для обох, вміст цієї рядка в кінцевому підсумку оцінюється як код оболонки, тому настільки sedж небезпечний, як evalце я говорю. sedпередається рядок, що містить uname(uname ще не виконується), і через виклик команди sed, команда uname закінчується виконанням. Як для eval. В sed 's/foo/$a/g', ви не передаєте unsanitized даних на sed, це не те , що ми говоримо тут.
Стефан Шазелас

2

Цей приклад може пролити трохи світла:

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

Вихід сценарію вище:

$VAR2
$VAR1
25

Дякуємо за ваш внесок, але, наскільки вони цікаві, ми насправді не зацікавлені у складанні списку (дещо інших) прикладів використання eval. Чи вважаєте ви, що існує якийсь фундаментальний, критичний аспект функціональності, evalякий не висвітлюється в існуючих відповідях? Якщо так, то поясніть це і використовуйте приклад, щоб проілюструвати своє пояснення. Будь ласка, не відповідайте на коментарі; відредагуйте  свою відповідь, щоб зробити її більш зрозумілою та повною.
Скотт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.