Я хочу динамически присваивать значения переменным, используя eval
. Следующий фиктивный пример работает:
var_name="fruit"
var_value="orange"
eval $(echo $var_name=$var_value)
echo $fruit
orange
Однако когда значение переменной содержит пробелы, eval
возвращается ошибка, даже если $var_value
она заключена в двойные кавычки:
var_name="fruit"
var_value="blue orange"
eval $(echo $var_name="$var_value")
bash: orange : command not found
Есть ли способ обойти это?
eval
этот путь неправильно. Вы расширяете,$var_value
прежде чем передать его,eval
что означает, что он будет интерпретироваться как шелл-код! (попробуйте, например, сvar_value="';:(){ :|:&};:'"
)eval
(это одна из причин, по которой я сказал, что вы не должны использоватьeval
).$var_value
- это инверсия кавычек - если принять безопасное значение для$var_name
(что на самом деле может быть столь же опасным, как и предположение) , тогда вы должны заключать двойные кавычки правой части в одинарные кавычки - не наоборот.eval
, используяprintf
и егоbash
специфичный%q
формат. Это все еще не рекомендация для использованияeval
, но я думаю, что это безопаснее, чем было раньше. Тот факт, что вам нужно приложить столько усилий, чтобы заставить его работать, является доказательством того, что вы должны использоватьdeclare
или использовать именованные ссылки.set -- a bunch of args; eval "process2 $(process1 "$@")"
гдеprocess1
просто печатать цитируемые числа, как"${1}" "${8}" "${138}"
. Это безумно просто - и так же просто, как и'"${'$((i=$i+1))'}" '
в большинстве случаев. Индексированные ссылки делают его безопасным, надежным и быстрым . Тем не менее - я проголосовал.Хороший способ работы с ним
eval
- заменить егоecho
для тестирования.echo
иeval
работать так же (если мы отложим\x
расширение, сделанное некоторымиecho
реализациями, подобнымиbash
's, при некоторых условиях).Обе команды объединяют свои аргументы с одним пробелом между ними. Разница в том, что
echo
отображает результат, в то время какeval
оценивает / интерпретирует результат как оболочку.Итак, чтобы увидеть, что код оболочки
оцените, вы можете запустить:
Это не то, что вы хотите, а то, что вы хотите:
Кроме того, использование
$(echo ...)
здесь не имеет смысла.Чтобы вывести выше, вы должны запустить:
Итак, чтобы интерпретировать это, это просто:
Обратите внимание, что он также может быть использован для установки отдельных элементов массива:
Как уже говорили другие, если вам не нужен
bash
конкретный код , вы можете использовать егоdeclare
как:Однако обратите внимание, что у него есть некоторые побочные эффекты.
Он ограничивает область действия переменной функцией, в которой она запущена. Поэтому вы не можете использовать ее, например, в таких вещах, как:
Потому что это объявило бы
foo
переменную, локальную дляsetvar
so, и было бы бесполезно.bash-4.2
добавлена-g
опция дляdeclare
объявить глобальную переменную, но это не то , что мы хотим , или как нашиsetvar
бы установить глобальный вар в противоположность тому , что вызывающего абонента , если вызывающий абонент является функцией, как в:который бы вывел:
Также обратите внимание, что, хотя
declare
вызываетсяdeclare
(фактическиbash
заимствовано понятие изtypeset
встроенной оболочки Korn ), если переменная уже установлена,declare
не объявляет новую переменную, и способ ее назначения зависит от типа переменной.Например:
приведет к другому результату (и потенциально может иметь неприятные побочные эффекты), если он
varname
был ранее объявлен как скаляр , массив или ассоциативный массив .источник
bash
которая недоступна (или когда вы понимаете, что вам нужна лучшая / более быстрая оболочка). Вeval
синтаксических работает во всех Bourne-подобных оболочек и является POSIX , поэтому все системы будут иметь ,sh
где это работает. (это также означает, что мой ответ применим ко всем оболочкам, и рано или поздно, как это часто случается здесь, вы увидите, что вопрос, не относящийся к bash, закрыт как дубликат этого вопроса.$var_name
содержит токены? ... как;
?eval
иdeclare
(думаюPATH
,TMOUT
,PS4
,SECONDS
...).export
. Я не следую за скобками в конце, хотя.Если вы делаете:
... и
$name
содержит;
- или любой из нескольких других токенов, которые оболочка может интерпретировать как разграничение простой команды - с предшествующим надлежащим синтаксисом оболочки, который будет выполнен.ВЫХОД
Однако иногда можно разделить оценку и выполнение таких утверждений. Например,
alias
может использоваться для предварительной оценки команды. В следующем примере определение переменной сохраняется вalias
том, что может быть успешно объявлено, только если$nm
оцениваемая переменная не содержит байтов, которые не соответствуют буквенно-цифровым или ASCII-символам_
.eval
используется здесь для обработки вызова новогоalias
из имени. Но он вызывается только в том случае, если предыдущееalias
определение было успешным, и, хотя я знаю, что множество различных реализаций будут принимать множество различных типов значений дляalias
имен, я еще не сталкивался с тем, который примет полностью пустое значение. ,Однако определение в
alias
for_$nm
предназначено для предотвращения перезаписи значимых значений среды. Я не знаю каких-либо заслуживающих внимания значений окружения, начинающихся с a,_
и обычно это безопасная ставка для полу-частного объявления.В любом случае, если
alias
определение будет успешным, оно объявитalias
имя для$nm
значения. Иeval
будет вызывать только это,alias
если также не начинается с числа - иначеeval
получает только нулевой аргумент. Таким образом, если оба условия выполняются,eval
вызывается псевдоним и создается определение переменной, сохраненное в псевдониме, после чего новоеalias
быстро удаляется из хеш-таблицы.источник
;
не допускается в именах переменных. Если у вас нет контроля над содержимым$name
, то вам необходимо очистить его и дляexport
/declare
. Хотяexport
код не выполняется, установка некоторых переменных, таких какPATH
,PS4
и многих из нихinfo -f bash -n 'Bash Variables'
имеет одинаково опасные побочные эффекты.eval
первом проходе - это расширение переменной. Как вы сказали в другом месте, в этом контексте это очень разрешено. Тем не менее, аргумент $ PATH очень хороший - я сделал небольшое редактирование и добавлю немного позже.zsh
,pdksh
,mksh
,yash
не жалуются наunset 'a;b'
.unset -v -- ...
.