LINUX.ORG.RU

Разбор ini-like файлов

 


0

2

Доброго времени суток to all.

Довольно часто приходится разбирать текстовые файлы, синтаксисом похожие на ini. «похожие» потому, что секции могут иметь другой вид (но явно выражены). Значение полей может отбиваться от их имени с помощью ":", вместо стандартного «=».

Пример секции:

ready >>> sh d SomeDevice
  deviceType = deviceModel
  ipAddress = x.x.x.x
  port = 5060
  userName = 

И, собственно, задача - распарсить такие файлы и занести соответствующие данные в БД (mysql). Сейчас пользуюсь довольно громоздкими конструкциями вида:

echo "insert into db.table (deviceName, deviceIp) values"
grep "(ready >>>|ipAddress)" "входящий файл" | sed -e 's/ready >>> sh d /section = /' | while read str; do
 echo "$str" | grep -q "section" && echo ""
 echo -n "$str; "
done | while read infoStr; do
 # infoStr - это строка, похожая на:
 # section="имя секции"; param1="value1"; param2=value2;
 section=""
 param1=""
 param2=""
 eval "${infoStr}"
 echo "('${section}', '${param1}', '${param2}'), "
done

Sql, генерируемый таким скриптом для секции из примера выше:

insert into db.table (deviceName, deviceIp) values
 ('SomeDevice', 'x.x.x.x'),

В итоге я получаю некоторый длинный запрос, который и выполняется в дальнейшем.

Так вот. Мб вместо таких чудовищь есть какое-нибудь «средство», которое бы позволило более проще обрабатывать «ini»-файлы?

Если нет требования именно на баше, можно сделать парсинг на питоне - привести заголовки к стандартному ini (например, регулярками или заранее sed'ом), потом распарсить стандартной либой. Можно потом либо сразу вписать в базу, либо передать по пайпу в баш. Как мне кажется, так будет быстрее, короче и надежнее.

К слову, питоновская парсилка ini считает «=» и ":" синонимами, что удобно.

lu4nik ★★★ ()

Через awk было-бы попроще.

#!/bin/awk -f

function printsectionifany() {
   if (section!="")
     print "('"section  "', '" ipAddress "'),"
}


BEGIN {
  print "insert into db.table (deviceName, deviceIp) values"
}
/^ready >>> sh d (.*)$/ {
  printsectionifany()
  section=$1
  ipAddress=""
}
/^\s*ipAddress\s*=\s*(.*)$/ {
  ipAddress=$1
}
END {
  printsectionifany()
}
┌[legolegs@battlehummer ~/programing/Free0N] :)
└> ./parce.awk < test.txt 
insert into db.table (deviceName, deviceIp) values
('ready', 'ipAddress'),

legolegs ★★★★★ ()
Ответ на: комментарий от lu4nik

Требования по языку реализации - нет. На рабочей машине все файлы обрабатываются.

На счет того, чтобы привести имя секции к стандартному виду - не подумал. Это же можно действительно sed-ом сделать. Спасибо.

А вот с питоном... Как указывать скрипту какие поля из ini-файла нужно заносить в БД? Как указывать скрипту если нужен маппинг полей (если имя свойства в ini-файле - ipAddress, а в базу нужно записывать в столбец ip)?

Опять же - это заморочки только для оптимизации. Сейчас имеется «шаблонный» bash-скрипт, который при необходимости в нужных местах исправляется. Под оптимизацией имею ввиду некоторый стандартный вариант использования инструмента.

Если брать питоновский скрипт (да хоть прогу на c++/java), то самый лучший вариант:

someUtility -f ini.file.path --section 'ready >>> sh d(.*)$' --map 'ip=ipAddress' --map '...' --table "db.table"'

О да! Это же можно сделать... довольно просто! Спасибо за наводку, пошёл кодить :)

Free0N ()
Ответ на: комментарий от legolegs

Получается на каждое свойство, которое нужно записывать в БД нужно отдельный блок /^\s*ipAddress\s*=\s*(.*)$/ { ipAddress=$1 } в скрипте? Или что-то еще добавить нужно?

insert into db.table (deviceName, deviceIp) values ('ready', 'ipAddress'),

тут должно быть «SomeDevice» вместо «ready»:

insert into db.table (deviceName, deviceIp) values('SomeDevice', 'ipAddress'),

Не могу найти как awk попросить вывести выбранную часть регулярки вместо $1

Free0N ()
Ответ на: комментарий от Free0N

Про приведение имени секции - можно вычитать файл, проитерировать по строкам, регуляркой сматчить названия секций, str.split'ом выделить имя и записать в строку. Потом ее можно скормить парсеру.

Также имей ввиду, что есть (нестандартные) либы для работы с мускулем из питона. Альтернативный вариант - вызвать из питона консольную утилиту для работы с мускулем. Но, из моего опыта работы с sqlite, это опасно в смысле инъекций.

Вообще, я бы сделал всю обработку в одном скрипте на питоне из соображений поддерживаемости.

lu4nik ★★★ ()
Последнее исправление: lu4nik (всего исправлений: 1)
Ответ на: комментарий от Rupricht

Эм... Она вроде только свой формат парсит (похожий на json). Или я чего-то не дочитал в доках?

Free0N ()
Ответ на: комментарий от Free0N

Вот правильный вариант на gawk. Далеко не так изящно, как я рассчитывал. Может и не нужн, раз там всё на питоне, но для полноты треда пусть будет.

#!/bin/awk -f

function printsectionifany() {
  if (section!="")
    printf "('%s', '%s'),\n", section, vars["ipAddress"]
}
BEGIN {
  FS="\\s*=\\s*"
  print "insert into db.table (deviceName, deviceIp) values"
}
/^ready >>> sh d (.*)$/ {
  match($0, /^ready >>> sh d (.*)$/, sec)
  printsectionifany()
  section=sec[1]
  delete vars
}
NF==2 {
  gsub(/^\s*|\s*$/,"")
  vars[$1]=$2
}
END {
  printsectionifany()
}

legolegs ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.