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


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'"'"

4
Питання в тому, чому в першу чергу команда зберігається в змінній? Ви будете краще збирати команду під час її запуску, зберігаючи аргументи параметрів у змінних або в масиві. Чи можете ви показати весь сценарій, який у вас є?
slhck

Відповіді:


18

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

Довга відповідь: це через порядок, в якому bash аналізує командний рядок. Зокрема, він шукає такі речі, як труби та переадресації, перш ніж розширювати змінні значення, і не повертається назад і не шукає труби тощо в розширених значеннях. По суті, змінні замінюються приблизно на півдорозі процесу розбору, тому значення отримується лише на півдорозі, перш ніж його виконати.

Для того, щоб вирішити це, вам потрібно відповісти на питання @ slhck: чому команда зберігається в змінній в першу чергу? Яку актуальну проблему ви намагаєтеся вирішити? Залежно від реальної мети, існує ряд можливих рішень:

  • Не зберігайте його у змінній, просто виконайте її безпосередньо. Зберігання команд для подальшого використання є складним, і якщо вам це не потрібно, просто не потрібно.

  • Використовуйте функцію замість змінної. Зрештою, для чого вони:

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

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

  • Використовуйте eval. Це слід вважати крайнім заходом, оскільки легко отримати несподівану поведінку. Він по суті запускає команду через інший повний прохід для розбору, тому всі труби тощо отримують повний сенс - але це також означає, що частини команди, які ви вважали лише даними, також будуть проаналізовані та, можливо, виконані. Імена файлів, що містять метахарактеристики оболонок («труба», крапка з комою, цитата / апостроф тощо), можуть мати дивні, а іноді й небезпечні наслідки. Якщо ви користуєтесь eval, принаймні двічі цитуйте рядок, інакше його вміст, по суті, проаналізується в півтора рази, з ще більш дивними результатами.

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

EDIT: Ще один стандартний підхід store-a-command-in-a-змінний - використовувати масив замість простої змінної. Це дозволяє без проблем зберігати команду зі складними аргументами (наприклад, що містять пробіли), але не зберігатиме такі речі, як труби та переадресації. Отже, підхід до масиву не буде корисним у цьому конкретному випадку.


+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 Я настійно рекомендую не використовувати evalдля цього - є занадто багато способів, щоб воно пішло не так. Кращий спосіб це зробити для масиву. Дивіться тут , тут , і ось приклади.
Гордон Девіссон

Дякую за пропозицію. Я спробую реалізувати рішення з масивом, щоб передати список виключень до rsync.
дентекс

4

Це має просто працювати:

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

evalБудьте обережні, що вводяться потенційні вразливості та несподівані ситуації з поведінкою, тому їх потрібно використовувати будь-коли з особливою обережністю.


Якщо ви все-таки використовуєте eval, вам дійсно потрібно використовувати подвійні лапки ( eval "$i"), щоб уникнути надзвичайно дивних ефектів. Наприклад, спробуйте це a="cat -nT index.php |grep ' .* '"(тобто регулярний вираз повинен відповідати двом пробілам з будь-якою послідовністю символів між ними) і подивитися, що насправді відбувається. Для додаткового кредиту поясніть, чому це відбувається.
Гордон Девіссон

@GordonDavisson додано подвійні цитати.
jlliagre

2

Декларуючи змінну, ви не повинні використовувати знак долара як префікс.

Спробуйте це:

i='echo hi'; $i

1
Це правильно, але насправді це не відповідь на питання ОП.
slhck

це для мене дійсно: P
nwgat
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.