Я посмотрел пару скриптов парсинга bash ini и увиделэтотone используется здесь несколько раз, поэтому я пытаюсь посмотреть, сработает ли это для меня. Похоже, что он считывает ini-файл построчно несколько раз и с каждым проходом постепенно создает функцию, которая в конечном итоге вычисляется. Он отлично работает для некоторых специальных символов, но не для других. Если значение в файле содержит одинарную кавычку или символ больше/меньше, скрипт возвращает синтаксические ошибки. Другие символы также создают неожиданные результаты. Как мне обрабатывать эти символы, когда они встречаются?
Это функция, которая анализирует ini-файл.
#!/usr/bin/env bash
cfg_parser ()
{
ini="$(<$1)" # read the file
ini="${ini//[/\[}" # escape [
ini="${ini//]/\]}" # escape ]
IFS=$'\n' && ini=( ${ini} ) # convert to line-array
ini=( ${ini[*]//;*/} ) # remove comments with ;
ini=( ${ini[*]/\ =/=} ) # remove tabs before =
ini=( ${ini[*]/=\ /=} ) # remove tabs be =
ini=( ${ini[*]/\ =\ /=} ) # remove anything with a space around =
ini=( ${ini[*]/#\\[/\}$'\n'cfg.section.} ) # set section prefix
ini=( ${ini[*]/%\\]/ \(} ) # convert text2function (1)
ini=( ${ini[*]/=/=\( } ) # convert item to array
ini=( ${ini[*]/%/ \)} ) # close array parenthesis
ini=( ${ini[*]/%\\ \)/ \\} ) # the multiline trick
ini=( ${ini[*]/%\( \)/\(\) \{} ) # convert text2function (2)
ini=( ${ini[*]/%\} \)/\}} ) # remove extra parenthesis
ini[0]="" # remove first element
ini[${#ini[*]} + 1]='}' # add the last brace
eval "$(echo "${ini[*]}")" # eval the result
}
ini-файл
[Section1]
value1=abc`def # unexpected EOF while looking for matching ``'
value2=ghi>jkl # syntax error near unexpected token `>'
value3=mno$pqr # executes ok but outputs "mnoqr"
value4=stu;vwx # executes ok but outputs "stu"
решение1
Тот факт, что выможетсделать что-то bash
не означает, что выдолжен.
sh
(и bash
т. д.) скрипты лучше всего подходят для относительно простых оболочек для запуска программ или команд обработки текста. Для более сложных задач, включая разбор ini-файлов и выполнение действий с ними, больше подойдут другие языки. Вы рассматривали возможность написания своего скрипта на perl
или python
? У обоих есть хорошие парсеры .ini-файлов - я несколько раз использовал модуль perl, Config::INI
когда мне нужно было разобрать ini-файл.
Но если вы все же хотите сделать это в bash, вам следует использовать ассоциативный массив вместо установки отдельных переменных.
Начните примерно с этого:
#! /bin/bash
inifile='user1074170.ini'
# declare $config to be an associative array
declare -A config
while IFS='=' read -r key val ; do
config["$key"]="$val"
done < <(sed -E -e '/^\[/d
s/#.*//
s/[[:blank:]]+$|^[[:blank:]]+//g' "$inifile" )
# now print out the config array
set | grep '^config='
Скрипт sed
удаляет [Section1]
строку (на самом деле, все строки, начинающиеся с открывающей квадратной скобки [
— вам нужно будет обрабатывать это по-другому [1] в ini-файле с несколькими разделами), удаляет комментарии, а также начальные и конечные пробелы. Цикл while
считывает каждую строку, используя =
в качестве разделителя полей, и присваивает содержимое переменным $key и $val, которые затем добавляются в массив $config.
Выход:
config=([value1]="abc\`def" [value3]="mno\$pqr" [value2]="ghi>jkl" [value4]="stu;vwx" )
Вы можете использовать записи массива позже в своем скрипте следующим образом:
$ echo value1 is "${config[value1]}"
value1 is abc`def
$ [ "${config[value4]}" = 'stu;vwx' ] && echo true
true
[1] awk
или perl
иметь удобные простые способы чтения файлов в режиме «абзац». Абзац определяется как блок текста, отделенный от других текстовых блоков одной или несколькими пустыми строками.
например, чтобы работать только с [Section1]
, вставьте awk
скрипт ниже непосредственно перед sed
скриптом, входящим в while
цикл выше:
awk -v RS= -v ORS='\n\n' '/\[Section1\]/' "$inifile" | sed ...
(и, конечно, удалите "$inifile"
из конца командной sed
строки — вы же не хотите снова передавать файл после того, как вы потрудились извлечь данные только [Section1]
из него).
Настройка ORS
не является строго обязательной, если вы извлекаете только один раздел из INI-файла, но будет полезна для сохранения разделения абзацев, если вы извлекаете два или более разделов.
решение2
Я знаю, что это неполный ответ, но, MySQL.lns
похоже, augeas способен разобрать большую часть этого. В augtool
:
augtool> set /augeas/load/testini/incl "/root/test.ini"
augtool> set /augeas/load/testini/lens "MySQL.lns"
augtool> load
augtool> ls /files/root/
.ssh/ test.ini/
augtool> ls /files/root/test.ini
target/ = Section1
augtool> ls /files/root/test.ini/target/
value1/ = abc`def
value2/ = ghi>jkl
value3/ = mno$pqr
value4/ = stu
Единственное, в чем он ошибся, это последний, и, честно говоря, я не думаю, что это ошибка. В .ini
файлах точка с запятой отмечает начало комментария. Я также хотел бы спросить, действительно ли ваши данные выглядят так.
Если это так, вы можете просто сделать что-то sed
до этого, что устанавливает ;
какое-то неиспользуемое значение символа, а затем преобразовать его обратно постобработкой. В конечном счете, вам понадобятся некоторые стандарты, чтобы файл мог иметь какую-либо различимую структуру.
РЕДАКТИРОВАТЬ:
Я протестировал это с помощью PHP-линзы и получил все, пока значения были заключены в кавычки:
[root@vlzoreman ~]# augtool
augtool> set /augeas/load/testini/lens "PHP.lns"
augtool> set /augeas/load/testini/incl "/root/test.ini"
augtool> load
augtool> ls /files/root/test.ini/Section1/
value1 = abc`def
value2 = ghi>jkl
value3 = mno$pqr
value4 = stu;vwx
В противном случае дело дошло бы до линзы MySQL.
ПРАВКА №2:
Я уверен, что есть более понятный способ записать это, но вот пример использования:
[root@vlp-foreman ~]# bash bash.sh
Values for: Section1:
:: value1 is abc`def
:: value2 is ghi>jkl
:: value3 is mno$pqr
:: value4 is stu;vwx
Values for: Section2:
:: value1 is abc`def
Сценарий:
#!/bin/bash
sections=$(augtool -A --transform "PHP.lns incl /root/test.ini" ls /files/root/test.ini | cut -f1 -d/)
for currentSection in $sections; do
echo "Values for: $currentSection:"
fields=$(augtool -A --transform "PHP.lns incl /root/test.ini" ls /files/root/test.ini/$currentSection | awk '{print $1}')
for currentField in $fields; do
currentValue=$(augtool -A --transform "PHP.lns incl /root/test.ini" print /files/root/test.ini/$currentSection/$currentField | cut -f2 -d=)
currentValue=$(echo $currentValue | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//' | sed -e 's/^"//' -e 's/"$//')
echo -e "\t:: $currentField is $currentValue"
done
done
решение3
Взгляни на crudini
:https://www.pixelbeat.org/programs/crudini/
в Ubuntu вы можете установить его с помощью sudo apt install crudini
для считывания значения из INI-файла, запустите:
$ value1=$(crudini --get "crudini.ini" "Section1" "value1")
crudini поддерживает несколько форматов INI-файлов и обрабатывает специальные символы:
crudini.ini
[Section1]
value1=abc`def
value2=ghi>jkl
value3=mno$pqr
value4=stu;vwx
чтение значений
$ for i in {1..4}; do crudini --get "crudini.ini" "Section1" "value$i"; done
abc`def
ghi>jkl
mno$pqr
stu;vwx
$