Официальная возможность получить лицензионный софт бесплатно.
Giveaway of the Day
Это не реклама!

Щелкните для получения прогноза по Биробиджану


среда, 9 сентября 2015 г.

bash: Возврат значений из функций и работа с файлами конфигурации

Удобно, когда скрипт берет настройки из некоего файла. Например, можно дать доступ к настройкам пользователю, не давая ему изменять собственно скрипт. Чтобы не повторять довольно длинный кусок кода в каждом скрипте каждый раз, когда надо получить какую-то настройку, лучше написать функцию, которая будет лезть в файл настроек и брать оттуда нужную переменную.
Тут вылезает другая грабля: функции в bash не умеют возвращать свои результаты. Максимум, на что они способны - вернуть код завершения. А как быть со строками?


На основании http://www.linuxjournal.com/content/return-values-bash-functions я нарисовал функцию getVar, которая хранится в отдельном файле и подключается в скриптах как source.
Эта функция читает определенный конфиг (задается в теле source-a), ищет в нем пару "переменная=значение" и возвращает "значение". Как возвращает? А просто пишет его на стандартный вывод, для перехвата которого используем $()

Переменная=$(getVar ИскомоеИмяПеременной)

Просто, ясно и никакой мистики. Может быть не так удобно, как было бы

Переменная=getVar(ИскомоеИмяПеременной)

но уж чем богаты, тому и рады.

До недавнего времени всё работало, но вот я решил поправить конфиг и добавил в него что-то вроде:

Переменная=$ПеременнаяОписаннаяВышеВЭтомКонфиге

Разумеется, такой вариант не прокатил, потому что это файл настроек, а не исполняемый файл, и вместо подстановки значения из ПОВВЭК, моя Переменная получала значение "$ПОВВЭК", что, по понятным причинам, меня не радовало.
Ладно, разобрался, закомментировал неправильное, написал как положено:

# Так нельзя: Переменная=$ПеременнаяОписаннаяВышеВЭтомКонфиге
Переменная=ЗначениеТакоеЖеКакУДругойПеременной


Запускаю сценарий, использующий Переменную, и выпадаю в осадок: она всё равно получает значение "$ПОВВЭК", которое закомментировано! Что за чертовщина? Чертовщина крылась в том, что я grep-ом искал первое появление имени Переменной в конфиге, и как-то не учел, что первым вполне может оказаться закомментированное появление.

Изначально выглядело это так:

grep -m 1 $1 $sourceConfig -q; if [ "$?" -ne "0" ]; then return 2; fi

Тихо ищем переменную в файле конфигурации до первого совпадения. Если grep вернул ошибку, то мы тоже сигналим об этом кодом завершения 2 (код 1 зарезервирован для случая, если по запарке функцию вызвали без параметров) и не возвращаем никакого значения. (возможно, потом сделаю слегка расширенный вариант функции, который будет принимать не обязательный второй параметр - значение по умолчанию, которое будет возвращаться, если переменной нет в конфиге).

В исправленном варианте пришлось делать конвейер подлиннее:

grep -vE '^[[:space:]]*#|^[[:space:]]*$' $sourceConfig|grep  -m 1 -E "^$1[[:space:]]*\=" -q; if [ "$?"...

Сначала пропускаем все строки, которые либо пустые, либо начинаются с диеза, перед которым может идти что-нибудь пробельное. В отобранном "материале" ищем довольно строго: искомое имя должно находиться в начале строки, и сразу после него должен быть знак равенства. Между именем и знаком может быть некоторое количество пробелов и/или табуляций для эстетов, выравнивающих столбцы в своих конфигах.

Поскольку Значение вполне может начинаться с пробела, то проверку на пробел справа от знака равенства я не делаю умышленно: всё, что идет справа от знака, возвращается как результат работы функции "as is".

Возможно, удастся обе предыдущие проверки спихнуть на awk, но сейчас мне лень, да и некогда этим заниматься. Желающие совершить сей подвиг могут отписаться в комментах.

Итак, окончательный текст файла source:

# Из этого файла берем нужное значение, если есть
sourceConfig=/путь/к/файлу/настроек

getVar ()
# http://www.linuxjournal.com/content/return-values-bash-functions
{
# если забыли передать параметр, выходим с кодом 1
if [ -z $1 ]; then return 1; fi

# если такой переменной нет, выходим с кодом 2
grep -vE '^[[:space:]]*#|^[[:space:]]*$' $sourceConfig|grep  -m 1 -E "^$1[[:space:]]*\=" -q; if [ "$?" -ne "0" ]; then return 2; fi
# значения в конфиге должны храниться в формате "переменная=значение", поэтому устанавливаем для awk разделитель полей "=" и выводим на stdout второе поле строки.
grep -vE '^[[:space:]]*#|^[[:space:]]*$' $sourceConfig|grep -m 1 -E "^$1[[:space:]]*\="|awk -F \= -e '{print $2}'
return 0
}

Комментариев нет:

Отправить комментарий

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