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

11

У меня есть команда, хранящаяся в переменной. Давайте представим, что переменная $iимеет значение:

cat -nT index.php |grep 'someregex'

Когда я пытаюсь выполнить указанную выше переменную, набирая $iее, происходит сбой, потому что оболочка пытается выполнить всю переменную как одну команду. Я также пытался использовать eval($i)и вставлять $iгалочки.

Как сделать так, чтобы оболочка выполнялась, $iкак если бы это была команда? И почему это не работает так же, как.

$i='echo hi'; $i

Это из-за того, что мне пришлось взламывать одинарные кавычки? (Потому что вы не можете их вложить.) В настоящее время мое решение

echo $i > /foo;  . /foo

Но я не хочу создавать файл только для этого, только чтобы удалить его позже.

Что я имею в виду под "я взломал в одиночных кавычках, я сделал:

$i='cat index.php | grep -P '"'"'MYREGEXHERE'"'"
Leathan
источник
4
Вопрос в том, почему команда хранится в переменной в первую очередь? Вам лучше собрать команду при ее запуске, сохраняя аргументы опции в переменных или в массиве. Можете ли вы показать весь сценарий у вас есть?
Slhck
Причина та же: superuser.com/questions/360966/…
Сиро Сантилли 冠状 病毒 审查 六四 事件 法轮功

Ответы:

18

Краткий ответ: см. BashAQ # 50: я пытаюсь поместить команду в переменную, но сложные случаи всегда терпят неудачу! ,

Длинный ответ: это из-за порядка, в котором bash анализирует командную строку. В частности, он ищет такие вещи, как каналы и перенаправления, прежде чем он расширяет значения переменных, и не возвращается и не просматривает каналы и т. Д. В расширенных значениях. По сути, переменные подставляются примерно в середине процесса синтаксического анализа, поэтому перед выполнением значение анализируется только наполовину.

Чтобы решить эту проблему, вам нужно ответить на вопрос @ slhck: почему команда хранится в переменной в первую очередь? Какую реальную проблему вы пытаетесь решить? В зависимости от конкретной цели, существует ряд возможных решений:

  • Не храните его в переменной, просто выполняйте его напрямую. Хранить команды для последующего использования сложно, а если вам не нужно, просто не делайте этого.

  • Используйте функцию вместо переменной. Вот для чего они, в конце концов:

    i() { cat -nT index.php |grep 'someregex'; }
    i

    Основным недостатком этого является то, что вы не можете создавать функцию динамически - вы не можете условно включать или исключать код из функции (хотя сама функция может иметь условные элементы, которые выбираются при ее выполнении).

  • Использование eval. Это следует считать последним средством, поскольку легко получить неожиданное поведение. По сути, он запускает команду через еще один полный этап синтаксического анализа, поэтому все каналы и т. Д. Получают свое полное значение, но это также означает, что части команды, которые вы считали просто данными, также будут проанализированы и, возможно, выполнены. Имена файлов, содержащие метасимволы оболочки (труба, точка с запятой, кавычка / апостроф и т. Д.), Могут иметь странные и иногда опасные последствия. Если вы используете eval, по крайней мере, заключите в кавычки строку, в противном случае ее содержимое, по сути, будет проанализировано полтора раза, с еще более странными результатами.

    i="cat -nT index.php |grep 'someregex'"
    eval "$i"

РЕДАКТИРОВАТЬ: Другой стандартный подход хранилища-команда-в-переменной заключается в использовании массива вместо простой переменной. Это позволяет без проблем хранить команды со сложными аргументами (например, содержащими пробелы), но не будет хранить такие вещи, как каналы и перенаправления. Таким образом, подход массива не будет полезен в этом конкретном случае.

Гордон Дэвиссон
источник
+1 за evalметод. Это помешало мне задать тот же вопрос :). У меня что-то подобное sudo rsync -aAHXi -n --delete-excluded --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/lost+found","/mnt/DATA/*","/var/log/*","/var/swap","/var/cache/apt/archives/*.deb"} -e ssh root@piac_wireless:/ /home/mrx/Docs/RPi/backup/piac/piac_usb-rootне выполнялось исключения правильно.
dentex
@dentex я бы настоятельно рекомендовал не использовать evalдля этого - слишком много способов ошибиться. Массив был бы лучшим способом сделать это. Смотрите здесь , здесь и здесь для примеров.
Гордон Дэвиссон
Спасибо за предложение. Я попытаюсь реализовать решение с помощью массива, чтобы передать список исключений в rsync.
dentex
4

Это должно просто работать:

a="cat -nT index.php |grep 'someregex'"
eval "$a"

Остерегайтесь, что evalпредставляет потенциальные уязвимости и неожиданные ситуации поведения, поэтому должны использоваться, если вообще, с особой осторожностью.

jlliagre
источник
Если вы используете eval, вам действительно нужно использовать двойные кавычки ( eval "$i"), чтобы избежать очень-очень-странных эффектов. Например, попробуйте это с помощью a="cat -nT index.php |grep ' .* '"(то есть регулярное выражение должно соответствовать двум пробелам с любой последовательностью символов между ними) и посмотрите, что на самом деле происходит. Для дополнительного кредита объясните, почему это происходит.
Гордон Дэвиссон
Добавлены двойные кавычки @GordonDavisson.
Jlliagre
2

При объявлении переменной не следует использовать знак доллара в качестве префикса.

Попробуй это:

i='echo hi'; $i
Miq
источник
1
Это верно, но это не ответ на вопрос ОП, на самом деле.
Slhck
это для меня РЕАЛЬНО: P
nwgat