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

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

#!/bin/sh
cmd="ls -la $APPROOTDIR | grep exception"
#1
$cmd
#2
eval "$cmd"

3 ответа

Решение

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

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

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

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

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

$ cmd="echo foo{bar,baz}"
$ $cmd
foo{bar,baz}
$ eval "$cmd"
foobar foobaz

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

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

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

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

Я думаю, вы должны поставить

`

(backtick) символы вокруг вашей переменной.

Другие вопросы по тегам