Как выполнить команду, хранящуюся в переменной?

83

Как правильно вызвать команду, хранящуюся в переменной?
Есть ли разница между 1 и 2?

#!/bin/sh
cmd="ls -la $APPROOTDIR | grep exception"
#1
$cmd
#2
eval "$cmd"
Владимир Безуглый
источник
7
См. BashFAQ / 050 .
Деннис Уильямсон

Ответы:

95

Оболочки Unix перед их выполнением выполняют серию преобразований в каждой строке ввода. Для большинства оболочек это выглядит примерно так (взято из bashсправочной страницы):

  • начальное разделение слов
  • расширение скобки
  • расширение тильды
  • параметр, переменная и арифметическое расширение
  • подстановка команд
  • вторичное разделение слов
  • расширение пути (также известное как globbing)
  • удаление цитаты

Использование $cmdнапрямую заменяет его вашей командой на этапе раскрытия параметров, а затем он претерпевает все последующие преобразования.

Использование eval "$cmd"ничего не делает до фазы удаления цитаты, где $cmdвозвращается как есть и передается в качестве параметра evalфункции, чья функция заключается в повторном запуске всей цепочки перед выполнением.

В общем, в большинстве случаев они одинаковы и различаются, когда ваша команда использует шаги преобразования вплоть до раскрытия параметров. Например, используя раскладку скобок:

$ cmd="echo foo{bar,baz}"
$ $cmd
foo{bar,baz}
$ eval "$cmd"
foobar foobaz
JB.
источник
6
Как обойтись eval "$cmd"без письма eval? $($cmd)? ${$cmd}?
Стивен Лу
2
@StevenLu, ни один из них не эквивалентен - намеренно: evalоперация анализирует данные как синтаксис; поэтому он очень чувствителен к безопасности, и делать это неявно было бы очень плохим тоном.
Чарльз Даффи
5

Если вы просто сделаете это, eval $cmd когда мы это сделаем cmd="ls -l"(в интерактивном режиме и в сценарии), мы получим желаемый результат. В вашем случае у вас есть канал с grep без шаблона, поэтому часть grep выйдет из строя с сообщением об ошибке. Просто $cmdсоздаст сообщение «команда не найдена» (или что-то подобное). Поэтому попробуйте использовать eval и использовать готовую команду, а не ту, которая генерирует сообщение об ошибке.

Хенно Брандсма
источник
Это была просто опечатка. Должно быть "исключение grep".
Владимир Безуглый
затем используйте eval, а не $ cmd сам по себе, так как он, вероятно, не сработает (этого не было в моем тестировании в bash и zsh). Это то, что должен был сделать eval ...
Хенно Брандсма
0

$cmdпросто заменит переменную ее значением, которое будет выполнено в командной строке. eval "$cmd"выполняет расширение переменных и подстановку команд перед выполнением результирующего значения в командной строке

Второй метод полезен, когда вы хотите запускать команды, которые не являются гибкими, например.
for i in {$a..$b}
Цикл форматирования не будет работать, потому что он не позволяет использовать переменные.
В этом случае обходной путь является использование канала для bash или eval.

Протестировано на Mac OSX 10.6.8, Bash 3.2.48

Зимба
источник
-4

Я думаю тебе стоит поставить

`

(обратная кавычка) вокруг вашей переменной.

Николодеон
источник
4
Это выполняет вывод команды, которая, например, в случае ls -l будет генерировать сообщение типа «total» command not found »(потому что total ... является частью вывода ls -l, например). НЕ то, что вы хотите.
Хенно Брандсма