После прочтения справочных страниц bash и относительно этого поста .
У меня все еще есть проблемы с пониманием того, что именно eval
делает команда, и что будет ее типичным использованием. Например, если мы делаем:
bash$ set -- one two three # sets $1 $2 $3
bash$ echo $1
one
bash$ n=1
bash$ echo ${$n} ## First attempt to echo $1 using brackets fails
bash: ${$n}: bad substitution
bash$ echo $($n) ## Second attempt to echo $1 using parentheses fails
bash: 1: command not found
bash$ eval echo \${$n} ## Third attempt to echo $1 using 'eval' succeeds
one
Что именно здесь происходит, и как знак доллара и обратная косая черта связаны с проблемой?
$($n)
работает$n
в подрубрике. Он пытается выполнить команду,1
которая не существует.echo $1
конечном итоге запустить , а не1
. Я не думаю, что это можно сделать с помощью подоболочек.eval
.Ответы:
eval
принимает строку в качестве аргумента и оценивает ее, как если бы вы ввели эту строку в командной строке. (Если вы передаете несколько аргументов, они сначала объединяются с пробелами между ними.)${$n}
это синтаксическая ошибка в bash. Внутри фигурных скобок у вас может быть только имя переменной с некоторыми возможными префиксами и суффиксами, но вы не можете иметь произвольный синтаксис bash и, в частности, вы не можете использовать расширение переменных. Есть способ сказать «значение переменной, имя которой находится в этой переменной», хотя:$(…)
выполняет команду, указанную в круглых скобках в подоболочке (т.е. в отдельном процессе, который наследует все параметры, такие как значения переменных из текущей оболочки), и собирает его выходные данные. Такecho $($n)
запускается$n
как команда оболочки и отображает ее вывод. Так как$n
оценивает1
,$($n)
пытается выполнить команду1
, которая не существует.eval echo \${$n}
запускает параметры , передаваемыеeval
. После расширения параметрыecho
и${1}
. Такeval echo \${$n}
работает командаecho ${1}
.Следует отметить , что большую часть времени, вы должны использовать двойные кавычки вокруг переменной замены и подстановки команд (то есть в любое время есть
$
):"$foo", "$(foo)"
. Всегда ставьте двойные кавычки вокруг переменных и подстановок команд , если только вы не знаете, что нужно их исключить. Без двойных кавычек оболочка выполняет разбиение поля (т. Е. Разделяет значение переменной или вывод команды на отдельные слова), а затем обрабатывает каждое слово как шаблон подстановки. Например:eval
не используется очень часто. В некоторых оболочках наиболее распространенным способом является получение значения переменной, имя которой неизвестно до времени выполнения. В bash это необязательно благодаря${!VAR}
синтаксису.eval
все еще полезно, когда вам нужно создать более длинную команду, содержащую операторы, зарезервированные слова и т. д.источник
eval
получает строку (которая сама может быть результатом анализа и анализа) и интерпретирует ее как фрагмент кода.eval eval
. Я не помню, чтобы когда-либо чувствовал потребность. Если вам действительно нужно дваeval
прохода, использовать временную переменную, это будет проще для отладки:eval tmp="\${$i}"; eval x="\${$tmp}"
.eval
просто берет код в строке и оценивает его по обычным правилам. Технически, это даже не правильно, потому что есть несколько угловых случаев, в которых Bash изменяет синтаксический анализ, чтобы даже не выполнять расширения аргументов eval - но это очень туманный лакомый кусочек, о котором, я сомневаюсь, кто-нибудь знает.Просто думайте о eval как о «оценке вашего выражения еще один раз перед выполнением»
eval echo \${$n}
становитсяecho $1
после первого тура оценки. Три изменения, чтобы заметить:\$
Стал$
(необходим обратный слэш, в противном случае он пытается оценить${$n}
, что означает переменную с именем{$n}
, которое не допускается)$n
был оценен в1
eval
исчезВо втором раунде это в основном то,
echo $1
что может быть выполнено напрямую.Поэтому
eval <some command>
сначала оценим<some command>
(под оценкой здесь я имею в виду замену переменных, замену экранированных символов на правильные и т. Д.), А затем снова запустим результирующее выражение.eval
используется, когда вы хотите динамически создавать переменные или читать выходные данные программ, специально предназначенных для чтения таким образом. См. Http://mywiki.wooledge.org/BashFAQ/048 для примеров. Ссылка также содержит некоторые типичные способыeval
использования и риски, связанные с этим.источник
${VAR}
синтаксис будет разрешен, и предпочтительно , когда есть какие - либо неоднозначности (делает$VAR == $V
, с последующимAR
или$VAR == $VA
последующимR
).${VAR}
эквивалентно$VAR
. На самом деле, это имя переменной$n
, которое не допускается.eval eval echo \\\${\${$i}}
сделаем тройную разыменование. Я не уверен, есть ли более простой способ сделать это. Кроме того,\${$n}
отлично работает (печатаетone
) на моей машине ..echo \\\${\${$i}}
печатает\${${n}}
.eval echo \\\${\${$i}}
эквивалентноecho \${${n}}`` and prints
$ {1}.
eval eval echo \\\ $ {\ $ {$ i}} `эквивалентноeval echo ${1}
и печатаетone
.` escapes the second one, and the third
`$
после этого ускользает . Так делается\${${n}}
после одного раунда оценки\\
получим одну обратную косую черту. Тогда\$
уступая один доллар. И так далее.По моему опыту, «типичное» использование eval - для запуска команд, которые генерируют команды оболочки для установки переменных среды.
Возможно, у вас есть система, которая использует набор переменных среды, и у вас есть скрипт или программа, которая определяет, какие из них следует установить, и их значения. Каждый раз, когда вы запускаете скрипт или программу, он запускается в разветвленном процессе, поэтому все, что он делает непосредственно с переменными среды, теряется при выходе. Но этот скрипт или программа может отправлять команды экспорта в stdout.
Без eval вам нужно будет перенаправить стандартный вывод во временный файл, создать временный файл и затем удалить его. С помощью eval вы можете просто:
Обратите внимание, что цитаты важны. Возьмите этот (надуманный) пример:
источник
${varname}
. Я считаю удобным использовать идентичные файлы .conf на нескольких разных серверах с несколькими параметрами, параметризованными переменными среды. Я отредактировал / opt / lampp / xampp (который запускает apache), чтобы сделать этот тип eval с помощью скрипта, который копается в системе и выводитexport
операторы bash для определения переменных для файлов .conf.eval "$(pyenv init -)"
в свой.bash_profile
(или аналогичный) файл конфигурации оболочки. Это создает небольшой сценарий оболочки, а затем оценивает его в текущей оболочке.Оператор eval указывает оболочке принять аргументы eval в качестве команды и выполнить их через командную строку. Это полезно в ситуации как ниже:
В вашем скрипте, если вы определяете команду в переменную, а затем хотите использовать эту команду, тогда вы должны использовать eval:
источник
Обновление: некоторые люди говорят, что следует всегда использовать 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
утверждения не имеют цели и могут быть исключены.eval
бессмысленно в первой строке, потому что в коде нет динамического аспекта, то есть он уже проанализирован в последних строках кода bash, поэтому он будет идентичен обычному выражению кода в сценарии bash. 2-й также неeval
имеет смысла, потому что, хотя есть шаг синтаксического анализа, конвертируемый$a
в его буквенный строковый эквивалент, нет никакого косвенного обращения (например, нет ссылки через строковое значение фактического существительного bash или переменной сценария, удерживаемого bash), поэтому он будет вести себя идентично в виде строки кода безeval
префикса.Пример 2:
Выполните присваивание переменных, используя имена переменных, переданные как строковые значения.
Если бы вы были
echo $key=$val
, результат был бы:Это , будучи конечным результатом синтаксического анализа строки, будет выполнено eval, следовательно, результатом оператора echo в конце ...
Пример 3:
Добавляем больше косвенности к примеру 2
Выше немного сложнее, чем в предыдущем примере, в большей степени полагаясь на порядок синтаксического анализа и особенности bash.
eval
Линия будет грубо получить разобран внутренне в следующем порядке (обратите внимание на следующие утверждения псевдокод, а не реальный код, просто попытка показать , как заявление будет ломаются на шаги внутри , чтобы прийти к конечному результату) .Если предполагаемый порядок синтаксического анализа не объясняет, что eval делает достаточно, третий пример может описать синтаксический анализ более подробно, чтобы помочь прояснить, что происходит.
Пример 4:
Узнайте, содержат ли переменные, имена которых содержатся в строках, сами строковые значения.
На первой итерации:
Bash анализирует аргумент
eval
иeval
видит это буквально во время выполнения:Следующий псевдокод пытается проиллюстрировать, как bash интерпретирует приведенную выше строку реального кода, чтобы получить окончательное значение, выполняемое с помощью
eval
. (следующие строки описательные, а не точные bash-коды):После того, как весь синтаксический анализ выполнен, результатом является то, что выполняется, и его эффект очевиден, демонстрируя, что в нем нет ничего особенно таинственного
eval
, и сложность заключается в анализе его аргумента.Оставшийся код в приведенном выше примере просто проверяет, является ли значение, присвоенное $ varval, нулевым, и, если это так, запрашивает у пользователя значение.
источник
Изначально я намеренно никогда не учился использовать eval, потому что большинство людей будут рекомендовать держаться подальше от него, как от чумы. Однако недавно я обнаружил случай использования, который заставил меня помазать лицо за то, что я не узнал его раньше.
Если у вас есть задания cron, которые вы хотите запустить для тестирования в интерактивном режиме, вы можете просмотреть содержимое файла с помощью cat, а затем скопировать и вставить задание cron для его запуска. К сожалению, это касается прикосновения мыши, что является грехом в моей книге.
Допустим, у вас есть работа cron по адресу /etc/cron.d/repeatme с содержимым:
*/10 * * * * root program arg1 arg2
Вы не можете выполнить это как скрипт со всем мусором перед ним, но мы можем использовать cut, чтобы избавиться от всего мусора, обернуть его в подоболочку и выполнить строку с eval
eval $( cut -d ' ' -f 6- /etc/cron.d/repeatme)
Команда cut распечатывает только 6-е поле файла, разделенное пробелами. Затем Eval выполняет эту команду.
В качестве примера я использовал задание cron, но концепция заключается в том, чтобы отформатировать текст из stdout, а затем оценить этот текст.
Использование eval в этом случае небезопасно, потому что мы точно знаем, что будем оценивать заранее.
источник
Мне недавно пришлось использовать,
eval
чтобы заставить несколько расширений скобок оцениваться в нужном мне порядке. Bash делает несколько расширений скобок слева направо, такрасширяется до
но мне нужно было сделать второе расширение скобки первым, давая
Лучшее, что я мог придумать, чтобы сделать это, было
Это работает, потому что одинарные кавычки защищают первый набор фигурных скобок от раскрытия во время синтаксического анализа
eval
командной строки, оставляя их для раскрытия посредством подоболочки, вызываемойeval
.Может быть какая-то хитрая схема с использованием вложенных фигурных скобок, которая позволяет этому происходить за один шаг, но если это так, я слишком стар и глуп, чтобы это увидеть.
источник
Вы спрашивали о типичных целях.
Одна распространенная жалоба на сценарии оболочки - то, что вы (предположительно) не можете перейти по ссылке, чтобы получить значения из функций.
Но на самом деле, через «Eval», вы можете перейти по ссылке. Вызываемый может передать список переменных назначений для оценки вызывающим. Он передается по ссылке, потому что вызывающая сторона может указать имя (имена) результирующей (ых) переменной (ей) - см. Пример ниже. Результаты ошибок могут быть переданы обратно стандартным именам, таким как errno и errstr.
Вот пример передачи по ссылке в bash:
Вывод выглядит так:
В этом текстовом выводе ширина полосы практически не ограничена ! И есть больше возможностей, если используются несколько выходных строк: например, первая строка может использоваться для назначения переменных, вторая для непрерывного «потока мысли», но это выходит за рамки этого поста.
источник
Мне нравится ответ «оцениваю свое выражение за один раз до исполнения», и я хотел бы уточнить на другом примере.
Любопытные результаты в варианте 2 заключаются в том, что мы передали бы 2 параметра следующим образом:
"value
content"
Как это для счетчика интуитивно? Дополнительное
eval
исправит это.Адаптировано с https://stackoverflow.com/a/40646371/744133
источник
В вопросе:
выводит ошибки, утверждая, что файлы a и tty не существуют. Я понял, что это означает, что tty не интерпретируется перед выполнением grep, а вместо этого bash передает tty в качестве параметра grep, который интерпретирует его как имя файла.
Существует также ситуация вложенного перенаправления, которая должна обрабатываться соответствующими скобками, которые должны указывать дочерний процесс, но bash примитивно является разделителем слов, создавая параметры для отправки в программу, поэтому круглые скобки не сопоставляются первыми, а интерпретируются как видел.
Я получил конкретный с grep, и указал файл в качестве параметра вместо использования канала. Я также упростил базовую команду, передавая выходные данные из команды в виде файла, чтобы трубопровод ввода-вывода не был вложенным:
работает хорошо.
не очень желательно, но устраняет вложенную трубу и также хорошо работает.
В заключение, bash, похоже, не любит вложенный pipping. Важно понимать, что bash не является программой новой волны, написанной рекурсивно. Вместо этого, bash - это старая программа 1,2,3, которая была дополнена функциями. В целях обеспечения обратной совместимости первоначальный способ интерпретации никогда не менялся. Если bash был переписан для соответствия первым круглым скобкам, сколько ошибок будет добавлено во сколько программ bash? Многие программисты любят быть загадочными.
источник