Что такое команда eval в bash?

177

Что вы можете сделать с evalкомандой? Почему это полезно? Это какая-то встроенная функция в bash? Там нет manстраницы для этого ..

LanceBaynes
источник
28
Используйте, type commandчтобы узнать, какой тип команды . ( type evalв этом случае)
rozcietrzewiacz
9
«Eval - это встроенная оболочка»
Нуно Рафаэль Фигейредо
3
help evalполучить страницу "man" из вашей оболочки
m-ric
eval встроен в bash и задокументирован на странице руководства bash. Так что просто наберите "man bash" и найдите соответствующий раздел для eval. Это относится и к другим bash-buildins тоже.
Дирк Таннхойзер

Ответы:

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

echox
источник
4
В терминологии оболочки evalэто встроенная функция, а не функция. На практике встроенные модули ведут себя во многом подобно функциям, которые не имеют определения на языке, но не совсем (что становится очевидным, если вы достаточно искривлены, чтобы определить вызываемую функцию eval).
Жиль
спасибо, исправил это :-)
echox
4
В чем разница между eval и backticks`?
JohnyTex
5
Обратные метки - это сокращение для выполнения еще одной целой оболочки, означающее, что в вашем скрипте будет запущен еще один дочерний процесс bash / sh.
Аарон Р.
Почему echo $ y (этап 3) возвращает foo (10) вместо $ foo? (т.е. просто значение foo вместо строки $ + значение foo)?
JohnDoea
60

Да, evalэто внутренняя команда bash, поэтому она описана на bashстранице руководства .

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команду должен быть тщательно проверен, чтобы избежать проблем безопасности.

andcoz
источник
18

evalне имеет man-страницы, потому что это не отдельная внешняя команда, а скорее встроенная оболочка, то есть команда, внутренняя и известная только shell ( 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это мощная команда, и если вы намереваетесь ее использовать, вы должны быть очень осторожны, чтобы не допустить возможных рисков безопасности, связанных с ней.

jw013
источник
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 вашего примера неправильный: это должно быть «Команда выше не работала, потому что bash пытался найти вызываемую команду 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 Shell Введение

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

На шаге 6 будет выполнен встроенный.
На шаге 6 eval вызывает отправку обработанной строки обратно на шаг 1.
Это единственное условие, при котором последовательность выполнения возвращается назад.

Вот почему я говорю: с eval входная строка анализируется дважды .

Эффекты разбора дважды.

Первый.

И самый важный эффект, чтобы понять. Является ли это следствием того, что в первый раз строка подвергается семи шагам оболочки, показанным выше, это цитирование . Внутри шага 4 (расширения) также есть последовательность шагов для выполнения всех расширений , последним из которых является Удаление цитаты :

Удаление цитаты всегда должно выполняться последним.

Таким образом, всегда есть один уровень цитирования удален.

Во-вторых.

Как следствие этого первого эффекта, дополнительные / различные части строки становятся доступными для разбора оболочки и всех других шагов.

Пример.

Косвенность.

Что позволяет выполнять косвенные расширения:

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

Почему? Потому что в первом цикле первый $цитируется.
Как таковой, он игнорируется для расширений оболочкой.
Следующий $с именем а раскрывается до «б».
Затем один уровень цитирования удаляется, в результате чего первый не $цитируется.
Конец первого цикла.

Затем во втором цикле строка $bчитается оболочкой.
Затем расширили до «с»
и дали в качестве аргумента echo.

Чтобы «увидеть», что eval будет производить в первом цикле (чтобы его можно было снова оценить), используйте echo. Или любая команда / скрипт / программа, которая четко показывает аргументы:

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

Замените eval на echo, чтобы «увидеть», что происходит:

$ 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. Как и любой встроенный.
Ищите «SHELL BUILTIN COMMANDS», а затем «eval».

Более простой способ получить помощь: в bash вы можете help 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'первых, вы не передаете неанимированные данные sed, это не то, о чем мы здесь говорим.
Стефан Шазелас
2

Этот пример может пролить некоторый свет:

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

Вывод скрипта выше:

$VAR2
$VAR1
25
LoMaPh
источник
Спасибо за ваш вклад, но, как бы они ни были интересны, нам не очень интересно составлять список (немного других) примеров использования eval. Верите ли вы, что есть какой-то фундаментальный, критический аспект функциональности, evalкоторый не описан в существующих ответах? Если так, то объясните это и используйте пример, чтобы проиллюстрировать ваше объяснение. Пожалуйста, не отвечайте в комментариях; отредактируйте  свой ответ, чтобы сделать его более понятным и полным.
Скотт