#regex #parsing #bash #variables
#регулярное выражение #синтаксический анализ #bash #переменные
Вопрос:
Допустим, у меня есть файл (php, как это бывает) с несколькими объявлениями переменных:
$dbuser = 'fred';
$dppass = 'abc123';
$dhhost = '127.0.0.1';
Что я хочу сделать с помощью BASH-скрипта, так это проанализировать этот файл, определить нужные мне переменные и прочитать их значения в переменные, к которым я могу получить доступ из моего BASH-скрипта.
Очевидно, что приведенный выше файл, являющийся PHP, содержит другие строки, которые меня не интересуют.
Я могу извлечь нужную мне информацию из оболочки bash, используя следующую команду:
grep $dbuser config.php.inc | grep -Po "'.*'" | cut -d ' -f 2
который аккуратно возвращает
fred
Но когда я пытаюсь добавить это в bash-скрипт, чтобы поместить выходные данные в переменную, используя обратные ссылки, следующим образом:
dbuser=`grep $dbuser config.php.inc | grep -Po "'.*'" | cut -d ' -f 2`
на этом этапе мой BASH-скрипт зависает.
Почему это зависает, или есть ли лучший способ сделать то, чего я пытаюсь достичь?
Ответ №1:
Попробуйте это таким образом:
dbuser=$(grep $dbuser config.php.inc | grep -Po "'.*'" | cut -d ' -f 2)
Причина, по которой это работает, а обратные подсказки — нет, заключается в том, как $ (command) обрабатывает цитирование по сравнению как обратные ссылки старого стиля обрабатывают цитирование.
Другими словами, следующая команда возврата сработала бы так же хорошо:
dbuser=`grep '$dbuser' config.php.inc | grep -Po "'.*'" | cut -d"'" -f 2`
- Использовал одинарные кавычки, чтобы заключить $dbuser, поскольку одинарные кавычки означают использование буквального текста, а не интерполяцию его как переменной оболочки.
- Удалено экранирование из .* поскольку оно не требуется.
- Удалено экранирование из команды cut, поскольку оно не требуется.
Кстати, это бы тоже сработало:
dbuser=`grep '$dbuser' config.php.inc | grep -Po "'.*'" | cut -d ' -f 2`
Кроме того, синтаксис $ (command) в целом является наилучшим подходом, когда это возможно. Используйте ` только из соображений переносимости, если вам необходимо поддерживать платформу, которая, как абсолютно точно известно, не поддерживает $ (command). Это, ИМХО, очень редко, поэтому эмпирическое правило — с самого начала ориентироваться на $ (command).
Комментарии:
1. Можете ли вы объяснить, почему это работает? Я думаю, что это действительно работает, но это очень тонко.
2. Это лучший метод, чем использование обратных ссылок?
3. По сути, да, $ (command) — лучший и более современный подход. Используйте обратные ссылки только тогда, когда вам нужно поддерживать платформу, которая, как известно, не поддерживает $ (command), что редко встречается в наши дни. Обратные ссылки имеют проблемы с менее чем тривиальным цитированием и действительно падают, когда вам нужно поддерживать несколько уровней вложенных команд. $ (command) хорошо справляется с этим.
4. Большое спасибо за объяснение, я не знал об этом и просто всегда использовал обратные ссылки. Я хотел бы, чтобы я мог принять все ответы на данный момент на вопрос, поскольку каждый из них предоставляет правильное решение.
Ответ №2:
Это вернет текст, подобный var='value';
awk '
match($1, /^$([[:alnum:]_] )=?/, m){
gsub(/^[^=] =[[:space:]]*/, "")
print m[1] "=" $0
}
' < file.php
Вы можете eval
получить результат.
Обновить
Это намного проще, чем описано выше. Я понял, что все, что вам нужно сделать, это удалить первое $
и убрать пробелы вокруг =
:
sed -e 's/$//' -e 's/ *= */=/' file.php
Комментарии:
1. Вау! Это аккуратный способ сделать это. Произойдет сбой в строке, которая вызывает функцию mysql_connect
$db=mysql_connect(...
, но я могу легко переключить это на другое включение, чтобы обойти это.2. … и под ошибкой я подразумеваю, что
eval
часть завершится неудачей.3.
sed
Версия работает не совсем так хорошо, поскольку она не справляется с другими строками в файле. К ним относятся<?php
,?>
и строки комментариев,ini_set
,require_once
а также несколько других строк, специфичных для PHP.4. Принято на основе awk-части ответа, поскольку она касается всех переменных в моем config.php.inc, без необходимости жестко кодировать каждую переменную.
5. @Брайан, я переходил к вашему образцу кода, который показывал только объявления переменных. Рад, что версия awk все же сработала для вас.
Ответ №3:
Похоже, что отсутствует
Проверьте, не \ $dbuser
Если у вас есть доступ к perl, попробуйте:
dbuser=$(perl -ne "print $1 if /$dbuser.*'(.*)'/" config.php.inc)
Примечание :
-e используйте следующий параметр как однострочный скрипт
-n используйте все параметры в качестве аргумента файла
выведите $ 1 распечатайте соответствующий шаблон при совпадении
Паратезис в регулярном выражении определяет группу захвата в 1 доллар.
Комментарии:
1. Добавление второго управляющего символа устранило проблему. На данный момент у меня нет доступного perl, но я мог бы попробовать. Определяет ли скобка ту часть, которая выводится из match?
Ответ №4:
С некоторой базовой проверкой и безопасностью
eval $(sed -n "s/^$([a-zA-Z][a-zA-Z0-9_]*) *= *'(.*)' *;/1='2';/p")
echo User:$dbuser Pass:$dppass Host:$dhhost
выведет для вашего примера
User:fred Pass:abc123 Host:127.0.0.1