вторник, 13 апреля 2010 г.

Bash и кавычки внутри переменной



Сегодня бился головой об стенку.

Имеем проблему: некий bash-скрипт подтягивает переменную из файла и передает ее как параметры на выполнение приложению. Грубо говоря, где-то так:

PARAMS="-par1 parameter"

/usr/local/bin/application $PARAMS


Собственно все элементарно и банально. Проблемы начались, когда в качестве параметра понадобилось передать значение, содержащее двойные кавычки и пробелы.


Т.е. нам нужно, чтобы работала вот такая конструкция:
PARAMS="-par1 \"para me ter\""

/usr/local/bin/application $PARAMS

Казалось бы, в чем там проблема? Приведенный код должен работать на ура.
Как бы не так! Приложение упорно не понимает, что ему подсунули.

Делаем echo вместо запуска - строка нормальная. мало того, если выведенную строку скормить интерпретатору - все работает!

Включаю дебаг и с удивлением обнаруживаю, что на самом деле передается вот что:
+ /usr/local/bin/application -par1 '"para' me 'ter"'
Обращаю внимание на одинарные кавычки, которые bash понатыкал где только мог.

Начались танцы с бубном, подбор кавычек, вариантов вывода и т.д. Одинарные кавычки не помогают - там еще все запущенней.
В общем, конечное решение, которое работает правильно:
PARAMS="-par1 \"para me ter\""

eval /usr/local/bin/application $PARAMS

7 комментариев:

  1. Спасибо за столь пространный ответ. Но вот в кое-чем я не совсем согласен, именно в свете "практических экспериментов"

    > Т.е. в вашем случае можно было бы написать вот так:
    > PARAMS='-par1 "para me ter"'

    Было испробовано в первую очередь :) И вот это вот как раз и не работает. Проверено. Приложение ругается на неверный параметр.

    > А можно было бы записать и вот так:
    > PARAMS="-par1 'para me ter'"

    Это тоже :) Было испробовано во-вторую очередь :)

    Работает только и исключительно eval.

    ОтветитьУдалить
  2. PAL, прочитайте повнимательней коментарий kirikaza, а именно третий обзац, а точнее "...Он вместо $PARAMS просто и тупо подставляет значение переменной PARAMS: [-par1 "para me ter"]. При этом это значение уже не интерпретируется по новой ― оно так же просто и тупо делится по разделителю (по пробелу в данном случае)..."
    т е содерщимое переменной $PARAMS повторно _НЕ_ разберается, а просто бъётся на слова...
    eval "застовляет инерпритатор интерпритировать" введённую строку, т е выполнить повторную обработку(разбор) содержимого переменной...

    С подобными проблемами пока не сталкивался, но на будущее буду знать. Спасибо!

    ОтветитьУдалить
  3. Насчёт вариантов '-par1 "para me ter"' и "-par1 'para me ter'": конечно же это не решает проблему и eval тут всё равно нужен; я имел в виду, что это означает ровным счётом то же самое, что и "-par1 \"para me ter\"", просто выглядит покрасивее! ;)

    Но есть вариант и не использовать eval: записать параметры в массив.

    PARAMS=(-par1 'para me ter')
    application "${PARAMS[@]}"

    Здесь PARAMS[@] означает «все элементы массива». Чтобы $ относился ко всему выражению, ставим выражение в фигубки: ${PARAMS[@]}. Иначе $PARAMS[@] эквивалентно ${PARAMS}[@], а это совсем не то. Ну а кавычки нужны, чтобы по пробелам внутри элементов массива не резалось.

    P.S.: О блоге: на тёмно синем фоне синяя клетка и светло-синие буквы... комменты трудно читаются; и зачем мой e-mail если всё равно на него не пришёл ваш ответ на мой коммент?

    ОтветитьУдалить
  4. > О блоге: на тёмно синем фоне синяя клетка и светло-синие буквы... комменты трудно читаются;

    Пофикшу на досуге. Была взята готовая тема и особо не подгонялась. Не рассчитывал вести пространные дискуссии :)

    > и зачем мой e-mail если всё равно на него не пришёл ваш ответ на мой коммент?

    нада, связано с премодерацией :).

    ОтветитьУдалить
  5. Advanced Bash-Scripting Guide
    5.2. Escaping
    http://www.tldp.org/LDP/abs/html/escapingsection.html

    У вас забанили Гуглю?

    ОтветитьУдалить
  6. > У вас забанили Гуглю?

    У нас, если Вы не заметили, личный блог, а не список ссылок из гугля.
    Личный блог с целью не гуглить полдня в поисках инфы.

    ОтветитьУдалить