Как я могу захватить значение INI в сценарии оболочки?
у меня есть параметры.ini-файл, например:
[parameters.ini]
database_user = user
database_version = 20110611142248
Я хочу прочитать и использовать версию базы данных, указанной в параметрах.ini-файл из сценария оболочки bash, поэтому я могу его обработать.
#!/bin/sh
# Need to get database version from parameters.ini file to use in script
php app/console doctrine:migrations:migrate $DATABASE_VERSION
Как бы я это сделал?
23 ответа:
Как насчет захвата для этой строки, а затем с помощью awk
version=$(awk -F "=" '/database_version/ {print }' parameters.ini)
вы можете использовать собственный синтаксический анализатор bash для интерпретации значений ini, по:
$ source <(grep = file.ini)
пример файла:
[section-a] var1=value1 var2=value2 IPS=( "1.2.3.4" "1.2.3.5" )
чтобы получить доступ к переменным, вы просто печатаете их:
echo $var1
. Вы также можете использовать массивы, как показано выше (echo ${IPS[@]}
).если вы хотите только одно значение просто grep для этого:
source <(grep var1 file.ini)
это просто, поскольку вам не нужна никакая внешняя библиотека для анализа данных, но она имеет некоторые недостатки. Для пример:
если у вас есть пробелы между
=
(имя и значение переменной), то вы должны сначала обрезать пробелы, например$ source <(grep = file.ini | sed 's/ *= */=/g')
в поддержку
;
комментарии, замените их на#
:$ sed "s/;/#/g" foo.ini | source /dev/stdin
разделы не поддерживаются (например, если вы
[section-name]
, то вы должны отфильтровать его, как показано выше, напримерgrep =
), то же самое для других неожиданных ошибок.Если вам нужно чтобы прочитать конкретное значение в определенном разделе, используйте
grep -A
,sed
,awk
илиex
).например.
source <(grep = <(grep -A5 '\[section-b\]' file.ini))
Примечание: Где
-A5
- это количество строк для чтения в разделе. Заменитьsource
Сcat
для отладки.если у вас есть какие-либо ошибки разбора, игнорировать их, добавив:
2>/dev/null
Читайте также: как разобрать и конвертировать ini файл в переменные в bash массив? в serverfault SE
Bash не предоставляет парсер для этих файлов, очевидно, вы можете использовать код awk или пару вызовов sed, но если вы bash-priest и не хотите больше ничего использовать, то вы можете попробовать следующий неясный код:
#!/usr/bin/env bash cfg_parser () { ini="$(<)" # 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 } cfg_writer () { IFS=' '$'\n' fun="$(declare -F)" fun="${fun//declare -f/}" for f in $fun; do [ "${f#cfg.section}" == "${f}" ] && continue item="$(declare -f ${f})" item="${item##*\{}" item="${item%\}}" item="${item//=*;/}" vars="${item//=*/}" eval $f echo "[${f#cfg.section.}]" for var in $vars; do echo $var=\"${!var}\" done done }
использование:
# parse the config file called 'myfile.ini', with the following # contents:: # [sec2] # var2='something' cfg.parser 'myfile.ini' # enable section called 'sec2' (in the file [sec2]) for reading cfg.section.sec2 # read the content of the variable called 'var2' (in the file # var2=XXX). If your var2 is an array, then you can use # ${var[index]} echo "$var2"
bash ini-parser можно найти по адресу сайт блога старой школы DevOps.
просто включите ваш .ini-файл в тело bash:
File пример.ini:
DBNAME=test DBUSER=scott DBPASSWORD=tiger
File example.sh
#!/bin/bash #Including .ini file . example.ini #Test echo "${DBNAME} ${DBUSER} ${DBPASSWORD}"
Sed one-liner, который учитывает разделы. Пример файла:
[section1] param1=123 param2=345 param3=678 [section2] param1=abc param2=def param3=ghi [section3] param1=000 param2=111 param3=222
скажем, вы хотите param2 из section2. Выполните следующие действия:
sed -nr "/^\[section2\]/ { :l /^param2[ ]*=/ { s/.*=[ ]*//; p; q;}; n; b l;}" ./file.ini
даст вам
def
одним из более возможных решений
dbver=$(sed -n 's/.*database_version *= *\([^ ]*.*\)//p' < parameters.ini) echo $dbver
все решения, которые я видел до сих пор, также попали в закомментированные строки. Это не так, если код комментария
;
:awk -F '=' '{if (! ( ~ /^;/) && ~ /database_version/) print }' file.ini
отображение значения my_key в ini-стиль my_file:
sed -n -e 's/^\s*my_key\s*=\s*//p' my_file
-n
-- ничего не печатайте по умолчанию-e
-- выполнить выражениеs/PATTERN//p
-- отображение всего, что следует этой схеме В шаблоне:^
-- картины начинается в начале строки\s
-- пробельный символ*
-- ноль или много (пробельные символы)пример:
$ cat my_file # Example INI file something = foo my_key = bar not_my_key = baz my_key_2 = bing $ sed -n -e 's/^\s*my_key\s*=\s*//p' my_file bar
так:
найти шаблон, где строка начинается с нуля или много пробелов символов, далее следует строка my_key, затем ноль или много пробелов, знак равенства, затем ноль или много пробелов снова. Отображение остальной части содержимого в этой строке в соответствии с этим шаблоном.
для людей (таких как я), которые хотят читать INI - файлы из сценариев оболочки (read shell, а не bash) - я создал небольшую вспомогательную библиотеку, которая пытается сделать именно это:
https://github.com/wallyhall/shini (лицензия MIT, делайте с ней, как вам угодно. Я связал выше, включая его встроенный, поскольку код довольно длинный.)
Это несколько более сложная, чем просто
sed
строки, предложенные выше - но работает на очень похожем основа.функция читает в файле построчно-ищет маркеры раздела (
[section]
) и "ключ/значение" заявления (key=value
).в конечном итоге вы получаете обратный вызов вашей собственной функции - раздел, ключ и значение.
Вы можете использовать
crudini
инструмент для получения значений ini, например:DATABASE_VERSION=$(crudini --get parameters.ini '' database_version)
можно использовать
sed
чтобы проанализировать файл конфигурации ini, особенно если у вас есть имена разделов, такие как:# last modified 1 April 2001 by John Doe [owner] name=John Doe organization=Acme Widgets Inc. [database] # use IP address in case network name resolution is not working server=192.0.2.62 port=143 file=payroll.dat
таким образом, вы можете использовать следующее
sed
скрипт для разбора выше данных:# Configuration bindings found outside any section are given to # to the default section. 1 { x s/^/default/ x } # Lines starting with a #-character are comments. /#/n # Sections are unpacked and stored in the hold space. /\[/ { s/\[\(.*\)\]// x b } # Bindings are unpacked and decorated with the section # they belong to, before being printed. /=/ { s/^[[:space:]]*// s/[[:space:]]*=[[:space:]]*/|/ G s/\(.*\)\n\(.*\)/|/ p }
это позволит преобразовать исходные данные в плоском формате:
owner|name|John Doe owner|organization|Acme Widgets Inc. database|server|192.0.2.62 database|port|143 database|file|payroll.dat
так будет проще разобрать с помощью
sed
,awk
илиread
, имея имена разделов в каждой строчке.кредиты и источник:файлы конфигурации для раковины скрипты Майкл Грюневальд
некоторые из ответов не уважают комментарии. Некоторые не уважают разделы. Некоторые распознают только один синтаксис (только ":" или только "="). Некоторые ответы Python терпят неудачу на моей машине из-за различной капитализации или неспособности импортировать модуль sys. Все они слишком лаконичны для меня.
поэтому я написал свой собственный, и если у вас есть современный Python, вы, вероятно, можете назвать это из своей оболочки Bash. Он имеет то преимущество, что придерживается некоторых общих конвенций кодирования Python, и даже предоставляет разумные сообщения об ошибках и помощь. Чтобы использовать его, назовите его что-то вроде myconfig.py (Не называйте это configparser.py или он может попытаться импортировать себя,) сделайте его исполняемым и назовите его как
value=$(myconfig.py something.ini sectionname value)
вот мой код для Python 3.5 на Linux:
#!/usr/bin/env python3 # Last Modified: Thu Aug 3 13:58:50 PDT 2017 """A program that Bash can call to parse an .ini file""" import sys import configparser import argparse if __name__ == '__main__': parser = argparse.ArgumentParser(description="A program that Bash can call to parse an .ini file") parser.add_argument("inifile", help="name of the .ini file") parser.add_argument("section", help="name of the section in the .ini file") parser.add_argument("itemname", help="name of the desired value") args = parser.parse_args() config = configparser.ConfigParser() config.read(args.inifile) print(config.get(args.section, args.itemname))
вот моя версия, которая анализирует разделы и заполняет глобальный ассоциативный массив g_iniProperties С ним. Обратите внимание, что это работает только с Баш В4.2 и выше.
function parseIniFile() { #accepts the name of the file to parse as argument () #declare syntax below (-gA) only works with bash 4.2 and higher unset g_iniProperties declare -gA g_iniProperties currentSection="" while read -r line do if [[ $line = [* ]] ; then if [[ $line = [* ]] ; then currentSection=$(echo $line | sed -e 's/\r//g' | tr -d "[]") fi else if [[ $line = *=* ]] ; then cleanLine=$(echo $line | sed -e 's/\r//g') key=$currentSection.$(echo $cleanLine | awk -F: '{ st = index(,"=");print substr(,0,st-1)}') value=$(echo $cleanLine | awk -F: '{ st = index(,"=");print substr(,st+1)}') g_iniProperties[$key]=$value fi fi; done < }
и вот пример кода с использованием функции выше:
parseIniFile "/path/to/myFile.ini" for key in "${!g_iniProperties[@]}"; do echo "Found key/value $key = ${g_iniProperties[$key]}" done
этот скрипт получит следующие параметры:
это означает, что если ваш ini есть :
pars_ini.ksh
например. как это назвать :
[ среда ]
a=x
[ DataBase_Sector]
DSN = что-то
затем вызов :
pars_ini.ksh / users/bubu_user / parameters.ini DataBase_Sector DSN
Это позволит получить следующее "что-то"
скрипт " pars_ini.КШ" :
\#!/bin/ksh \#INI_FILE=path/to/file.ini \#INI_SECTION=TheSection \# BEGIN parse-ini-file.sh \# SET UP THE MINIMUM VARS FIRST alias sed=/usr/local/bin/sed INI_FILE= INI_SECTION= INI_NAME= INI_VALUE="" eval `sed -e 's/[[:space:]]*\=[[:space:]]*/=/g' \ -e 's/;.*$//' \ -e 's/[[:space:]]*$//' \ -e 's/^[[:space:]]*//' \ -e "s/^\(.*\)=\([^\"']*\)$/=\"\"/" \ < $INI_FILE \ | sed -n -e "/^\[$INI_SECTION\]/,/^\s*\[/{/^[^;].*\=.*/p;}"` TEMP_VALUE=`echo "$"$INI_NAME` echo `eval echo $TEMP_VALUE`
моя версия однострочного
#!/bin/bash #Reader for MS Windows 3.1 Ini-files #Usage: inireader.sh # e.g.: inireader.sh win.ini ERRORS DISABLE # would return value "no" from the section of win.ini #[ERRORS] #DISABLE=no INIFILE= SECTION= ITEM= cat $INIFILE | sed -n /^\[$SECTION\]/,/^\[.*\]/p | grep "^[:space:]*$ITEM[:space:]*=" | sed s/.*=[:space:]*//
только что закончил писать свой собственный парсер. Я попытался использовать различные Парсеры, найденные здесь, ни один из них не работает как с ksh93 (AIX), так и с bash (Linux).
Это старый стиль программирования - разбор построчно. Довольно быстро, так как он использовал несколько внешних команд. Немного медленнее из-за всех eval, необходимых для динамического имени массива.
ini поддерживает 3 специальных синтаксиса:
- includefile=ini-файла --> Загрузите дополнительный ini-файл. Полезно для разделения ini в нескольких файлах, или повторно использовать некоторые части конфигурации
- includedir=каталог --> То же самое, что и includefile, но включает полный каталог
- includesection=section --> Скопируйте существующий раздел в текущий раздел.
я использовал весь синтаксис thoses, чтобы иметь довольно сложный, многоразовый ini-файл. Полезно устанавливать продукты при установке новой ОС-мы делаем это много.
значения могут быть доступны с помощью ${ini[$section.$пункт.}] Массив должен быть определены перед вызовом этого.
получать удовольствие. Надеюсь, что это полезно для кого-то другого!
function Show_Debug { [[ $DEBUG = YES ]] && echo "DEBUG $@" } function Fatal { echo "$@. Script aborted" exit 2 } #------------------------------------------------------------------------------- # This function load an ini file in the array "ini" # The "ini" array must be defined in the calling program (typeset -A ini) # # It could be any array name, the default array name is "ini". # # There is heavy usage of "eval" since ksh and bash do not support # reference variable. The name of the ini is passed as variable, and must # be "eval" at run-time to work. Very specific syntax was used and must be # understood before making any modifications. # # It complexify greatly the program, but add flexibility. #------------------------------------------------------------------------------- function Load_Ini { Show_Debug "($@)" typeset ini_file="" # Name of the array to fill. By default, it's "ini" typeset ini_array_name="${2:-ini}" typeset section variable value line my_section file subsection value_array include_directory all_index index sections pre_parse typeset LF=" " if [[ ! -s $ini_file ]]; then Fatal "The ini file is empty or absent in [$ini_file]" fi include_directory=$(dirname $ini_file) include_directory=${include_directory:-$(pwd)} Show_Debug "include_directory=$include_directory" section="" # Since this code support both bash and ksh93, you cannot use # the syntax "echo xyz|while read line". bash doesn't work like # that. # It forces the use of "<<<", introduced in bash and ksh93. Show_Debug "Reading file $ini_file and putting the results in array $ini_array_name" pre_parse="$(sed 's/^ *//g;s/#.*//g;s/ *$//g' <$ini_file | egrep -v '^$')" while read line; do if [[ ${line:0:1} = "[" ]]; then # Is the line starting with "["? # Replace [section_name] to section_name by removing the first and last character section="${line:1}" section="${section%\]}" eval "sections=${$ini_array_name[sections_list]}" sections="$sections${sections:+ }$section" eval "$ini_array_name[sections_list]=\"$sections\"" Show_Debug "$ini_array_name[sections_list]=\"$sections\"" eval "$ini_array_name[$section.exist]=YES" Show_Debug "$ini_array_name[$section.exist]='YES'" else variable=${line%%=*} # content before the = value=${line#*=} # content after the = if [[ $variable = includefile ]]; then # Include a single file Load_Ini "$include_directory/$value" "$ini_array_name" continue elif [[ $variable = includedir ]]; then # Include a directory # If the value doesn't start with a /, add the calculated include_directory if [[ $value != /* ]]; then value="$include_directory/$value" fi # go thru each file for file in $(ls $value/*.ini 2>/dev/null); do if [[ $file != *.ini ]]; then continue; fi # Load a single file Load_Ini "$file" "$ini_array_name" done continue elif [[ $variable = includesection ]]; then # Copy an existing section into the current section eval "all_index=\"${!$ini_array_name[@]}\"" # It's not necessarily fast. Need to go thru all the array for index in $all_index; do # Only if it is the requested section if [[ $index = $value.* ]]; then # Evaluate the subsection [section.subsection] --> subsection subsection=${index#*.} # Get the current value (source section) eval "value_array=\"${$ini_array_name[$index]}\"" # Assign the value to the current section # The $value_array must be resolved on the second pass of the eval, so make sure the # first pass doesn't resolve it ($value_array instead of $value_array). # It must be evaluated on the second pass in case there is special character like , # or ' or " in it (code). eval "$ini_array_name[$section.$subsection]=\"$value_array\"" Show_Debug "$ini_array_name[$section.$subsection]=\"$value_array\"" fi done fi # Add the value to the array eval "current_value=\"${$ini_array_name[$section.$variable]}\"" # If there's already something for this field, add it with the current # content separated by a LF (line_feed) new_value="$current_value${current_value:+$LF}$value" # Assign the content # The $new_value must be resolved on the second pass of the eval, so make sure the # first pass doesn't resolve it ($new_value instead of $new_value). # It must be evaluated on the second pass in case there is special character like , # or ' or " in it (code). eval "$ini_array_name[$section.$variable]=\"$new_value\"" Show_Debug "$ini_array_name[$section.$variable]=\"$new_value\"" fi done <<< "$pre_parse" Show_Debug "exit ($@)\n" }
эта реализация использует
awk
и имеет следующие преимущества:
- возвращает только первую запись
- игнорирует строки, которые начинаются с
;
- обрезает начальные и конечные пробелы, но не внутренние пробельные символы
отформатированная версия:
awk -F '=' '/^\s*database_version\s*=/ { sub(/^ +/, "", ); sub(/ +$/, "", ); print ; exit; }' parameters.ini
шутка:
awk -F '=' '/^\s*database_version\s*=/ { sub(/^ +/, "", ); sub(/ +$/, "", ); print ; exit; }' parameters.ini
когда я использую пароль в base64, я ставлю разделитель ":" потому что строка base64 может иметь "=". Например (я использую
ksh
):> echo "Abc123" | base64 QWJjMTIzCg==
на
parameters.ini
поставить строкуpass:QWJjMTIzCg==
и наконец:> PASS=`awk -F":" '/pass/ {print }' parameters.ini | base64 --decode` > echo "$PASS" Abc123
если в строке есть пробелы типа
"pass : QWJjMTIzCg== "
добавить| tr -d ' '
обрезать их:> PASS=`awk -F":" '/pass/ {print }' parameters.ini | tr -d ' ' | base64 --decode` > echo "[$PASS]" [Abc123]
Как и другие ответы Python, вы можете сделать это с помощью
-c
флаг для выполнения последовательности инструкций Python, заданных в командной строке:$ python3 -c "import configparser; c = configparser.ConfigParser(); c.read('parameters.ini'); print(c['parameters.ini']['database_version'])" 20110611142248
это имеет то преимущество, что требуется только стандартная библиотека Python и преимущество не писать отдельный файл сценария.
Я написал быстрый и простой скрипт python для включения в мой скрипт bash.
например, ваш ini-файл называется
food.ini
а в файле вы можете иметь несколько разделов и несколько строк:[FRUIT] Oranges = 14 Apples = 6
скопируйте этот небольшой 6-строчный скрипт Python и сохраните его как
configparser.py
#!/usr/bin/python import configparser import sys config = configpParser.ConfigParser() config.read(sys.argv[1]) print config.get(sys.argv[2],sys.argv[3])
теперь в вашем сценарии bash вы можете сделать это, например.
OrangeQty=$(python configparser.py food.ini FRUIT Oranges)
или
ApplesQty=$(python configparser.py food.ini FRUIT Apples) echo $ApplesQty
это предполагает:
- у Вас установлен Python
- у вас установлена библиотека configparser (это должно поставляться с установкой std python)
надеюсь, что это помогает :)
при этом используется системный perl и чистые регулярные выражения:
cat parameters.ini | perl -0777ne 'print "" if /\[\s*parameters\.ini\s*\][\s\S]*?\sdatabase_version\s*=\s*(.*)/'
ответ "Карен Габриелян" среди других ответов был лучшим, но в некоторых средах у нас нет awk, как типичный busybox, я изменил ответ ниже кода.
trim() { local trimmed="" # Strip leading space. trimmed="${trimmed## }" # Strip trailing space. trimmed="${trimmed%% }" echo "$trimmed" } function parseIniFile() { #accepts the name of the file to parse as argument () #declare syntax below (-gA) only works with bash 4.2 and higher unset g_iniProperties declare -gA g_iniProperties currentSection="" while read -r line do if [[ $line = [* ]] ; then if [[ $line = [* ]] ; then currentSection=$(echo $line | sed -e 's/\r//g' | tr -d "[]") fi else if [[ $line = *=* ]] ; then cleanLine=$(echo $line | sed -e 's/\r//g') key=$(trim $currentSection.$(echo $cleanLine | cut -d'=' -f1')) value=$(trim $(echo $cleanLine | cut -d'=' -f2)) g_iniProperties[$key]=$value fi fi; done < }