Я пытаюсь преобразовать ini-файл в переменные массива bash. Пример файла ini приведен ниже:
[foobar]
session=foo
path=/some/path
[barfoo]
session=bar
path=/some/path
так они становятся:
session[foobar]=foo
path[foobar]=/some/path
session[barfoo]=bar
и так далее.
Прямо сейчас я мог придумать только эту команду
awk -F'=' '{ if ($1 ~ /^\[/) section=$1; else if ($1 !~ /^$/) print $1 section "=" $2 }'
Еще одна проблема в том, что он не занимает места рядом с =
во внимание. думаю sed
вероятно, лучше подходит для этой работы, но я не знаю, как хранить и хранить временную переменную для имени раздела в sed
.
Есть идеи, как это сделать?
Я бы использовал простой скрипт Python для этой работы, поскольку он встроен в INI парсер:
#!/usr/bin/env python
import sys, ConfigParser
config = ConfigParser.ConfigParser()
config.readfp(sys.stdin)
for sec in config.sections():
print "declare -A %s" % (sec)
for key, val in config.items(sec):
print '%s[%s]="%s"' % (sec, key, val)
а затем в bash:
#!/bin/bash
# load the in.ini INI file to current BASH - quoted to preserve line breaks
eval "$(cat in.ini | ./ini2arr.py)"
# test it:
echo ${barfoo[session]}
Конечно, в awk есть более короткие реализации, но я думаю, что это более читабельно и проще в обслуживании.
Gawk принимает регулярные выражения в качестве разделителей полей. Следующее исключает пробелы вокруг знака равенства, но сохраняет их в остальной части строки. Вокруг значения добавляются кавычки, поэтому эти пробелы, если они есть, сохраняются при выполнении присваивания Bash. Я предполагаю, что имена разделов будут числовыми переменными, но если вы используете Bash 4, было бы легко адаптировать это для использования ассоциативных массивов с самими именами разделов в качестве индексов.
awk -F ' *= *' '{ if ($1 ~ /^\[/) section=$1; else if ($1 !~ /^$/) print $1 section "=" "\"" $2 "\"" }'
Обратите внимание, что вы также можете удалить пробелы, которые показывает Халед (только для $ 1 и раздела), поскольку имена переменных Bash не могут содержать пробелы.
Также этот метод не будет работать, если значения содержат знаки равенства.
Другой способ - использовать Bash while read
цикл и выполнять назначения по мере чтения файла, используя declare
который защищен от наиболее вредоносного контента.
foobar=1
barfoo=2 # or you could increment an index variable each time a section is found
while IFS='= ' read var val
do
if [[ $var == \[*] ]]
then
section=$var
elif [[ $val ]]
then
declare "$var$section=$val"
fi
done < filename
Опять же, ассоциативные массивы довольно легко поддерживаются.
Если вы хотите убрать лишние пробелы, вы можете использовать встроенную функцию gsub
. Например, вы можете добавить:
gsub(/ /, "", $1);
Это удалит все пробелы. Если вы хотите удалить пробелы в начале или конце токена, вы можете использовать
gsub(/^ /, "", $1);
gsub(/ $/, "", $1);
Всегда предполагая, что у Python есть ConfigParser, можно создать вспомогательную функцию оболочки следующим образом:
get_network_value()
{
cat <<EOF | python
import ConfigParser
config = ConfigParser.ConfigParser()
config.read('network.ini')
print (config.get('$IFACE','$param'))
EOF
}
$IFACE
и $param
являются разделом соответственно параметром.
Затем этот помощник разрешает такие вызовы, как:
address=`param=address get_network_value` || exit 1
netmask=`param=netmask get_network_value` || exit 1
gateway=`param=gateway get_network_value` || exit 1
Надеюсь это поможет!
Вот чистое решение bash.
Это новая и улучшенная версия того, что опубликовал chilladx:
https://github.com/albfan/bash-ini-parser
Для действительно простого следования начальному примеру: после загрузки просто скопируйте файлы bash-ini-parser
, и scripts/file.ini
в тот же каталог, затем создайте клиентский тестовый скрипт, используя приведенный ниже пример для этого же каталога.
source ./bash-ini-parser
cfg_parser "./file.ini"
cfg_section_sec2
echo "var2=$var2"
echo "var5[*]=${var5[*]}"
echo "var5[1]=${var5[1]}"
Вот еще несколько улучшений, которые я внес в сценарий bash-ini-parser ...
Если вы хотите иметь возможность читать ini-файлы с окончанием строки Windows, а также Unix, добавьте эту строку в функцию cfg_parser сразу после той, которая читает файл:
ini=$(echo "$ini"|tr -d '\r') # remove carriage returns
Если вы хотите читать файлы с ограниченными правами доступа, добавьте эту необязательную функцию:
# Enable the cfg_parser to read "locked" files
function sudo_cfg_parser {
# Get the file argument
file=$1
# If not "root", enable the "sudo" prefix
sudoPrefix=
if [[ $EUID -ne 0 ]]; then sudoPrefix=sudo; fi
# Save the file permissions, then "unlock" the file
saved_permissions=$($sudoPrefix stat -c %a $file)
$sudoPrefix chmod 777 $file
# Call the standard cfg_parser function
cfg_parser $file
# Restore the original permissions
$sudoPrefix chmod $saved_permissions $file
}
Если у вас есть Git и вы согласны с ограничением, заключающимся в том, что не можете использовать символы подчеркивания в именах ключей, вы можете использовать git config
как синтаксический анализатор / редактор INI общего назначения.
Он будет обрабатывать парсинг пары ключ / значение из =
и отбросить несущественные пробелы, плюс вы получите комментарии (оба ;
и #
) и приведение типов в основном бесплатно. Я включил полный рабочий пример ввода OP .ini
и желаемый результат (ассоциативные массивы Bash) ниже.
Однако, учитывая такой конфигурационный файл
; mytool.ini
[section1]
inputdir = ~/some/dir
enablesomefeature = true
enablesomeotherfeature = yes
greeting = Bonjour, Monde!
[section2]
anothersetting = 42
… При условии, что вам просто нужно быстрое и грязное решение, и вы не связаны с идеей наличия настроек в ассоциативном массиве Bash, вы могли бы уйти всего лишь с помощью:
eval $(git config -f mytool.ini --list | tr . _)
# or if 'eval' skeeves you out excessively
source <(git config -f mytool.ini --list | tr . _)
который создает переменные среды с именем sectionname_variablename
в нынешних условиях. Это, конечно, работает только в том случае, если вы можете быть уверены, что ни одно из ваших значений никогда не будет содержать точку или пробел (более надежное решение см. Ниже).
Получение произвольных значений с использованием функции оболочки для экономии ввода:
function myini() { git config -f mytool.ini; }
Здесь тоже можно использовать псевдоним, но он обычно не раскрывается в сценарии оболочки [1], и в любом случае псевдонимы заменяются функциями оболочки «почти для всех целей», [2], согласно Башу страница руководства.
myini --list
# result:
# section1.inputdir=~/some/dir
# section1.enablesomefeature=true
# section1.enablesomeotherfeature=yes
# section2.anothersetting=42
myini --get section1.inputdir
# result:
# ~/some/dir
С --type
вариант, вы можете "канонизировать" определенные настройки как целые числа, логические значения или пути (автоматически ~
):
myini --get --type=path section1.inputdir # value '~/some/dir'
# result:
# /home/myuser/some/dir
myini --get --type=bool section1.enablesomeotherfeature # value 'yes'
# result:
# true
Сделайте все переменные в mytool.ini
доступно как SECTIONNAME_VARIABLENAME
в текущей среде с сохранением внутренних пробелов в ключевых значениях:
source <(
git config -f mytool.ini --list \
| sed 's/\([^.]*\)\.\(.*\)=\(.*\)/\U\1_\2\E="\3"/'
)
Что делает выражение sed на английском языке:
\1
, затем\2
, и\3
В \U
и \E
последовательности в строке замены (в верхнем регистре эта часть строки замены) являются GNU sed
расширение. В macOS и BSD вы просто используете несколько -e
выражения для достижения того же эффекта.
Работа со встроенными кавычками и пробелами в раздел имена (которые git config
позволяет) остается в качестве упражнения для читателя. :)
Дано:
; foo.ini
[foobar]
session=foo
path=/some/path
[barfoo]
session=bar
path=/some/path
Это приведет к результату, который запрашивает OP, просто переставив некоторые захваты в выражении замены sed, и будет нормально работать без GNU sed:
source <(
git config -f foo.ini --list \
| sed 's/\([^.]*\)\.\(.*\)=\(.*\)/declare -A \2["\1"]="\3"/'
)
Я предполагаю, что с цитированием в реальном мире могут возникнуть проблемы. .ini
файл, но он работает для предоставленного примера. Результат:
declare -p {session,path}
# result:
# declare -A session=([barfoo]="bar" [foobar]="foo" )
# declare -A path=([barfoo]="/some/path" [foobar]="/some/path" )