Як виконати команду, що зберігається у змінній?


83

Який правильний спосіб викликати якусь команду, що зберігається у змінній?
Чи є відмінності між 1 і 2?

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

Відповіді:


95

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

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

Використання $cmdбезпосередньо замінює його на вашу команду під час фази розширення параметрів, а потім воно зазнає всіх наступних перетворень.

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

Отже, в основному вони однакові в більшості випадків і різняться, коли ваша команда використовує кроки перетворення аж до розширення параметрів. Наприклад, за допомогою розширення фігурної дужки:

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

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 ...
Henno Brandsma

0

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

Другий метод корисний, коли ви хочете запускати команди, які не є гнучкими, наприклад.
for i in {$a..$b}
Цикл формату не буде працювати, оскільки він не допускає змінних.
У цьому випадку труба для удару чи виправлення є обхідним шляхом.

Перевірено на Mac OSX 10.6.8, Bash 3.2.48


-4

Я думаю, вам слід поставити

`

(зворотні позначки) навколо вашої змінної.


4
Це виконує вихідні дані команди, яка, наприклад, у випадку ls -l генерує повідомлення типу "total" команду не знайдено "(оскільки total ... є частиною результату ls -l, наприклад) Отже, це НЕ те, що ви хочете.
Хенно Брандсма
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.