Обновление: некоторые люди говорят, что следует всегда использовать eval. Я не согласен. Я думаю, что риск возникает, когда можно передать коррумпированный вклад eval
. Однако есть много распространенных ситуаций, когда это не риск, и поэтому стоит знать, как использовать eval в любом случае. Этот ответ на стекопоток объясняет риски eval и альтернативы eval. В конечном счете, пользователь должен определить, является ли eval безопасным и эффективным в использовании.
Оператор bash eval
позволяет вам выполнять строки кода, рассчитанные или полученные вашим bash-скриптом.
Возможно, самым простым примером была бы программа bash, которая открывает другой сценарий bash в виде текстового файла, читает каждую строку текста и использует их eval
для выполнения по порядку. По сути, это то же самое поведение, что и в source
операторе bash , и это то, что можно использовать, если только в этом нет необходимости выполнять какие-либо преобразования (например, фильтрацию или замену) содержимого импортируемого сценария.
Мне это редко требовалось eval
, но я нашел полезным читать или записывать переменные, имена которых содержались в строках, присвоенных другим переменным. Например, выполнять действия с наборами переменных, сохраняя при этом небольшой объем кода и избегая избыточности.
eval
концептуально просто. Однако строгий синтаксис языка bash и порядок синтаксического анализа интерпретатора bash могут быть нюансированы и могут eval
показаться загадочными, сложными в использовании или понимании. Вот основы:
Переданный аргумент eval
является строковым выражением , которое вычисляется во время выполнения. eval
выполнит окончательный анализируемый результат своего аргумента как фактическую строку кода в вашем скрипте.
Синтаксис и порядок разбора являются строгими. Если результат не является исполняемой строкой кода bash, в области действия вашего скрипта программа завершится сбоем в eval
операторе при попытке выполнить мусор.
При тестировании вы можете заменить eval
утверждение на echo
и посмотреть, что отображается. Если это допустимый код в текущем контексте, его выполнение eval
будет работать.
Следующие примеры могут помочь прояснить, как работает eval ...
Пример 1:
eval
оператор перед «нормальным» кодом является NOP
$ eval a=b
$ eval echo $a
b
В приведенном выше примере первые eval
утверждения не имеют цели и могут быть исключены. eval
бессмысленно в первой строке, потому что в коде нет динамического аспекта, то есть он уже проанализирован в последних строках кода bash, поэтому он будет идентичен обычному выражению кода в сценарии bash. 2-й также не eval
имеет смысла, потому что, хотя есть шаг синтаксического анализа, конвертируемый $a
в его буквенный строковый эквивалент, нет никакого косвенного обращения (например, нет ссылки через строковое значение фактического существительного bash или переменной сценария, удерживаемого bash), поэтому он будет вести себя идентично в виде строки кода без eval
префикса.
Пример 2:
Выполните присваивание переменных, используя имена переменных, переданные как строковые значения.
$ key="mykey"
$ val="myval"
$ eval $key=$val
$ echo $mykey
myval
Если бы вы были echo $key=$val
, результат был бы:
mykey=myval
Это , будучи конечным результатом синтаксического анализа строки, будет выполнено eval, следовательно, результатом оператора echo в конце ...
Пример 3:
Добавляем больше косвенности к примеру 2
$ keyA="keyB"
$ valA="valB"
$ keyB="that"
$ valB="amazing"
$ eval eval \$$keyA=\$$valA
$ echo $that
amazing
Выше немного сложнее, чем в предыдущем примере, в большей степени полагаясь на порядок синтаксического анализа и особенности bash. eval
Линия будет грубо получить разобран внутренне в следующем порядке (обратите внимание на следующие утверждения псевдокод, а не реальный код, просто попытка показать , как заявление будет ломаются на шаги внутри , чтобы прийти к конечному результату) .
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
Если предполагаемый порядок синтаксического анализа не объясняет, что eval делает достаточно, третий пример может описать синтаксический анализ более подробно, чтобы помочь прояснить, что происходит.
Пример 4:
Узнайте, содержат ли переменные, имена которых содержатся в строках, сами строковые значения.
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
На первой итерации:
varname="myvarname_a"
Bash анализирует аргумент eval
и eval
видит это буквально во время выполнения:
eval varval=\$$myvarname_a
Следующий псевдокод пытается проиллюстрировать, как bash интерпретирует приведенную выше строку реального кода, чтобы получить окончательное значение, выполняемое с помощью eval
. (следующие строки описательные, а не точные 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)
После того, как весь синтаксический анализ выполнен, результатом является то, что выполняется, и его эффект очевиден, демонстрируя, что в нем нет ничего особенно таинственного eval
, и сложность заключается в анализе его аргумента.
varval="User-provided"
Оставшийся код в приведенном выше примере просто проверяет, является ли значение, присвоенное $ varval, нулевым, и, если это так, запрашивает у пользователя значение.
$($n)
работает$n
в подрубрике. Он пытается выполнить команду,1
которая не существует.