Bash: ini-файл в массив [РЕШЕНО] (Парсинг INI-файла в массив)

На самом деле это единственный раздел про unix на этом форуме

Модераторы: /dev/random, Модераторы разделов

Ответить
Novascriptum
Сообщения: 40

Bash: ini-файл в массив [РЕШЕНО]

Сообщение Novascriptum »

Всем доброго времени и с наступающим Новым годом! :drinks:

Имеем рабочую функцию парсинга INI-файлов:

Код: Выделить всё

get_ini()
{
    while IFS='= ' read var val
    do
        if [[ $var == \[*] ]]
        then
            section=`echo "$var" | tr -d "[] "`
        elif [[ $val ]]
        then
            eval $section$var="$val"
        fi
    done < $1
}


Работает следующим образом. Пусть есть INI-файл следующего вида:

Код: Выделить всё

[section1]
var1=val1
var2=val2

[section2]
var3=val3
var4=var4
...


Тогда в нашем скрипте вызываем функцию:

Код: Выделить всё

get_ini /path/to/ini.ini

... и получаем в коде доступными переменные вида $section1var1.

Хочу преобразовать эту функцию, чтобы она писала секции в массивы, т.е. в коде становились бы доступными переменные $section1[var1], $section1[var2] и т.д.
Почему-то никак не получается. Пробовал вызывать в функции

Код: Выделить всё

eval $section[$var]="$val"

но не работает.
Как сделать правильно?
Спасибо сказали:
allez
Сообщения: 2223
Статус: Не очень злой админ :-)
ОС: SuSE, CentOS, FreeBSD, Windows

Re: Bash: ini-файл в массив [РЕШЕНО]

Сообщение allez »

Ага, вы пытаетесь использовать ассоциативные массивы, они же хеши. А вы свой массив объявили с помощью команды declare -A?

P. S. Вот для затравки немного информации по хешам в bash: http://ru.wikipedia.org/wiki/Bash#.D0.90.D....B8.D0.B2.D1.8B
Спасибо сказали:
Novascriptum
Сообщения: 40

Re: Bash: ini-файл в массив [РЕШЕНО]

Сообщение Novascriptum »

Спасибо, учел. Возник еще вопрос - почему-то эта функция игнорирует последнюю строку в ini-файле - не записывает ее в переменную.
Если поставить в конце пустую строку, то все хорошо. Если же в последней строке какой-то параметр (var=value), он не добавляется. Куда копать?
Спасибо сказали:
Аватара пользователя
Bizdelnick
Модератор
Сообщения: 20793
Статус: nulla salus bello
ОС: Debian GNU/Linux

Re: Bash: ini-файл в массив [РЕШЕНО]

Сообщение Bizdelnick »

Вообще не ставить перевод строки в конце файла - моветон. На этом многие парсеры обламываются.
read читает строку, заканчивающуюся символом перевода строки. Нет символа - нет строки. Тут предлагают решение: http://stackoverflow.com/questions/1548555...le-line-by-line
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:
Novascriptum
Сообщения: 40

Re: Bash: ini-файл в массив [РЕШЕНО]

Сообщение Novascriptum »

С declare, кстати, все оказалось непросто:

Код: Выделить всё

get_ini()
{
    local inifile="$1"
    local prefix="$2"


    while IFS='= ' read var val
    do
        if [[ $var == \[*] ]]
        then
            section=`echo "$var" | tr -d "[] "`
            #declare -A $prefix$section
        elif [[ $val ]]
        then
            eval $prefix$section[$var]="$val"
        fi
    done < $inifile
}

От первоначального варианта отличается закомментированной строкой "declare -A $prefix$section" и вводом переменной $prefix.

Declare в данном случае не сработал. Как я выяснил, я просто неправильно обращался к ключу массива. Вместо правильного

Код: Выделить всё

echo ${section["var"]}
использовал

Код: Выделить всё

echo $section["var"]

Если использовать Declare, то функция вообще перестает работать (не записывает глобальные переменные). Видимо, функция объявляет переменные массивов локальными.

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

Код: Выделить всё

[section1]
var1=value1
var2=value2

[section2]
var3=value3
var4=value4


В скрипте пишем:

Код: Выделить всё

get_ini /path/to/ini.ini INI
echo ${INIsection1["var1"]}
echo ${INIsection1["var2"]}
echo ${INIsection2["var3"]}
echo ${INIsection2["var4"]}

Имеем результат:

Код: Выделить всё

value2
value2
value4
value4


Теперь добавим в саму функцию get_ini после строчки с eval строку с выводом:

Код: Выделить всё

eval $prefix$section[$var]="$val"
echo "'$prefix$section[$var]' => '$val'"


Результат из функции будет такой:

Код: Выделить всё

'INIsection1[var1]' => 'value1'
'INIsection1[var2]' => 'value2'
'INIsection2[var3]' => 'value3'
'INIsection2[var4]' => 'value4'

Т.е. из функции все правильно, из окружающего ее вызов скрипта нет.
Результат для меня неожиданный. Почему так - и как исправить?
Спасибо сказали:
allez
Сообщения: 2223
Статус: Не очень злой админ :-)
ОС: SuSE, CentOS, FreeBSD, Windows

Re: Bash: ini-файл в массив [РЕШЕНО]

Сообщение allez »

Novascriptum писал(а):
21.12.2013 22:13
echo ${INIsection2["var4"]}

Э-э... так у вас же в разделе section2 нет параметра var4:
Novascriptum писал(а):
21.12.2013 22:13
[section2]
var2=value3
var3=value4

Или это просто очепятка?
Спасибо сказали:
Novascriptum
Сообщения: 40

Re: Bash: ini-файл в массив [РЕШЕНО]

Сообщение Novascriptum »

allez писал(а):
21.12.2013 22:25
Или это просто очепятка?

Была опечатка, уже поправил. :rolleyes: Вопрос остался актуален.
Спасибо сказали:
Аватара пользователя
/dev/random
Администратор
Сообщения: 5289
ОС: Gentoo

Re: Bash: ini-файл в массив [РЕШЕНО]

Сообщение /dev/random »

Novascriptum писал(а):
21.12.2013 22:13
Т.е. из функции все правильно, из окружающего ее вызов скрипта нет.
Результат для меня неожиданный. Почему так - и как исправить?

Потому что вы не объявили массив. Команда declare здесь обязательна. Если вы хотите, чтобы массив был глобальным, используйте опцию -g, только учтите, что она появилась сравнительно недавно, и в старых версиях баша её нет.

Да, и ещё такой момент: я очень надеюсь, что недоверенные пользователи не могут отредактировать ваш конфиг. Потому что при попытке прочитать такой файл:

Код: Выделить всё

[testtesttest]
foo=bar rm -rf /*
ваш скрипт выполнит команду "rm -rf /*".
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current
Контактная информация:

Re: Bash: ini-файл в массив [РЕШЕНО]

Сообщение drBatty »

Novascriptum писал(а):
21.12.2013 22:13
Видимо, функция объявляет переменные массивов локальными.

нет. Переменная всегда глобальная, кроме случая, когда вы объявили её как local

в вашем случае вы вообще НЕ объявили свой массив. declare должно быть в самом начале, ВНЕ функции.

И да, лучше не нужно такого извращения, сделайте просто файл-конфиг типа

Код: Выделить всё

key1="value1"
key2="value2"

и включите его в скрипт командой source.

а у Патрега так:

Код: Выделить всё

# /etc/rc.d/rc.inet1.conf
#
# This file contains the configuration settings for network interfaces.
# If USE_DHCP[interface] is set to "yes", this overrides any other settings.
# If you don't have an interface, leave the settings null ("").

# You can configure network interfaces other than eth0,eth1... by setting
# IFNAME[interface] to the interface's name. If IFNAME[interface] is unset
# or empty, it is assumed you're configuring eth<interface>.

# Several other parameters are available, the end of this file contains a
# comprehensive set of examples.

# =============================================================================

# Config information for eth0:
IPADDR[0]="192.168.1.1"
NETMASK[0]="255.255.255.0"
USE_DHCP[0]="no"
DHCP_HOSTNAME[0]=""

# Config information for eth1:
IPADDR[1]=""
NETMASK[1]=""
USE_DHCP[1]=""
DHCP_HOSTNAME[1]=""

...
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
Аватара пользователя
/dev/random
Администратор
Сообщения: 5289
ОС: Gentoo

Re: Bash: ini-файл в массив [РЕШЕНО]

Сообщение /dev/random »

drBatty писал(а):
24.12.2013 14:24
Novascriptum писал(а):
21.12.2013 22:13
Видимо, функция объявляет переменные массивов локальными.

нет. Переменная всегда глобальная, кроме случая, когда вы объявили её как local

Ошибаетесь. Команда declare, вызванная без опции -g, тоже объявляет переменную как локальную.
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current
Контактная информация:

Re: Bash: ini-файл в массив [РЕШЕНО]

Сообщение drBatty »

/dev/random писал(а):
24.12.2013 14:33
Ошибаетесь. Команда declare, вызванная без опции -g, тоже объявляет переменную как локальную.
Да? Возможно, спасибо.

Но я всё равно использую declare вне функций, т.ч. она у меня всё равно глобальная.
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
Novascriptum
Сообщения: 40

Re: Bash: ini-файл в массив [РЕШЕНО]

Сообщение Novascriptum »

Так как в моей версии bash (4.1.2) не поддерживается declare с опцией -g, то прибег к такому вот варианту решения:

Код: Выделить всё

    . inc/get_sections.sh
    . inc/get_ini.sh

    SECTION_LIST=$(get_sections $INIFILE "INI_")
    for SECTION in $SECTION_LIST
    do
        declare -A $SECTION
    done

    get_ini $INIFILE "INI_"


get_sections.sh:

Код: Выделить всё

#!/bin/bash
get_sections()
{
    local inifile="$1"
    local prefix="$2"

    while IFS='= ' read var val
    do
        if [[ $var == \[*] ]]
        then
            section=`echo "$var" | tr -d "[] "`
            echo $prefix$section
        fi
    done < $inifile
}


get_ini.sh:

Код: Выделить всё

#!/bin/bash
get_ini()
{
    local inifile="$1"
    local prefix="$2"

    while IFS='= ' read var val
    do
        if [[ $var == \[*] ]]
        then
            section=`echo "$var" | tr -d "[] "`
        elif [[ $val ]]
        then
            eval $prefix$section[$var]="$val"
        fi
    done < $inifile
}
Спасибо сказали:
Ответить