У меня есть файл parameters.ini, например:
[parameters.ini]
database_user = user
database_version = 20110611142248
Я хочу прочитать и использовать версию базы данных, указанную в файле parameters.ini, из оболочки bash script, чтобы я мог ее обработать.
#!/bin/sh
# Need to get database version from parameters.ini file to use in script
php app/console doctrine:migrations:migrate $DATABASE_VERSION
Как мне это сделать?
Как насчет grepping для этой строки, а затем awk
version=$(awk -F "=" '/database_version/ {print $2}' parameters.ini)
| tr -d ' '
в конце.
Вы можете использовать встроенный парсер 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
source <(grep = <(grep -A5 '\[section-b\]' file.ini))
это не будет работать для него: [sec a] a = 1 b = 2 c = 3 [сек b] a = 2 b = 3 [с c] a = 0. где нет определенного правила со строками
Bash не предоставляют парсер для этих файлов, очевидно, вы можете использовать awk-код или пару sed-вызовов, но если вы bash -priest и не хотите больше ничего использовать, вы можете попробуйте следующий неясный код:
#!/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
}
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 можно найти на веб-сайте в блоге Old School DevOps.
Просто включите ваш .ini файл в bash тело:
Файл example.ini:
DBNAME=test
DBUSER=scott
DBPASSWORD=tiger
Файл 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 из раздела2. Выполните следующие действия:
sed -nr "/^\[section2\]/ { :l /^param2[ ]*=/ { s/.*=[ ]*//; p; q;}; n; b l;}" ./file.ini
предоставит вам
def
"/^\[section2\]/,/^\[/{...}"
Все решения, которые я видел до сих пор, также затрагивают прокомментированные строки. Этого не было, если код комментария ;
:
awk -F '=' '{if (! ($0 ~ /^;/) && $0 ~ /database_version/) print $2}' file.ini
одно из возможных решений
dbver=$(sed -n 's/.*database_version *= *\([^ ]*.*\)/\1/p' < parameters.ini)
echo $dbver
Отобразите значение my_key в файле my_file ini:
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, за которой следуют ноль или многие пробельные символы, знак равенства, затем нуль или много символов пробелов. Покажите оставшуюся часть содержимого этой строки после этого шаблона.
bar
распечатаны), по крайней мере на Unix / OSX.
Для людей (например, я), которые хотят читать файлы INI из сценариев оболочки (читать оболочку, а не bash) - я выбил небольшую вспомогательную библиотеку, которая пытается сделать именно это:
https://github.com/wallyhall/shini (лицензия MIT, сделайте с ней, как вам будет угодно. Я связал выше, включая ее встроенную, поскольку код довольно длинный.)
Он несколько более "сложный", чем простые строки sed
, предложенные выше, но работают на очень схожей основе.
Функция читает файл в строке - ищет маркеры раздела ([section]
) и объявления ключа/значения (key=value
).
В конечном итоге вы получаете обратный вызов к своей собственной функции - раздел, ключ и значение.
Вы можете использовать crudini
инструмент, чтобы получить значения ini, например:
DATABASE_VERSION=$(crudini --get parameters.ini '' database_version)
Вот моя версия, которая анализирует разделы и заполняет им глобальный ассоциативный массив g_iniProperties. Обратите внимание, что это работает только с bash v4.2 и выше.
function parseIniFile() { #accepts the name of the file to parse as argument ($1)
#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($0,"=");print substr($0,0,st-1)}')
value=$(echo $cleanLine | awk -F: '{ st = index($0,"=");print substr($0,st+1)}')
g_iniProperties[$key]=$value
fi
fi;
done < $1
}
И вот пример кода с использованием функции выше:
parseIniFile "/path/to/myFile.ini"
for key in "${!g_iniProperties[@]}"; do
echo "Found key/value $key = ${g_iniProperties[$key]}"
done
Некоторые ответы не соответствуют комментариям. Некоторые не уважают разделы. Некоторые распознают только один синтаксис (только ":" или только "=" ). Некоторые ответы на 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))
Вы можете использовать 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
script для анализа выше данных:
# 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/\[\(.*\)\]/\1/
x
b
}
# Bindings are unpacked and decorated with the section
# they belong to, before being printed.
/=/ {
s/^[[:space:]]*//
s/[[:space:]]*=[[:space:]]*/|/
G
s/\(.*\)\n\(.*\)/\2|\1/
p
}
это преобразует данные ini в этот плоский формат:
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
, имея имена разделов в каждой строке.
Кредиты и источник: Файлы конфигурации для сценариев оболочки, Michael Grünewald
Как и другие ответы на Python, вы можете сделать это, используя флаг -c
для выполнения последовательности инструкций Python, заданных в командной строке:
$ python3 -c "import configparser; c = configparser.ConfigParser(); c.read('parameters.ini'); print(c['parameters.ini']['database_version'])"
20110611142248
Это имеет то преимущество, что требуется только стандартная библиотека Python и преимущество не писать отдельный файл script.
Этот script получит следующие параметры:
означает, что если ваш ini имеет:
pars_ini.ksh < путь к ini файлу > < имя сектора в файле Ini > < имя в имени = значение для возвратa >
например. как его называть:
[окружающая среда]
а = х
[DataBase_Sector]
DSN = что-то
Затем вызов:
pars_ini.ksh/users/bubu_user/parameters.ini DataBase_Sector DSN
это будет извлекать следующее "что-то"
\#!/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=$1
INI_SECTION=$2
INI_NAME=$3
INI_VALUE=""
eval `sed -e 's/[[:space:]]*\=[[:space:]]*/=/g' \
-e 's/;.*$//' \
-e 's/[[:space:]]*$//' \
-e 's/^[[:space:]]*//' \
-e "s/^\(.*\)=\([^\"']*\)$/\1=\"\2\"/" \
< $INI_FILE \
| sed -n -e "/^\[$INI_SECTION\]/,/^\s*\[/{/^[^;].*\=.*/p;}"`
TEMP_VALUE=`echo "$"$INI_NAME`
echo `eval echo $TEMP_VALUE`
Если Python доступен, следующие будут читать все разделы, ключи и значения и сохранять их в переменных с именами в формате "[раздел] _ [ключ]". Python может правильно читать файлы .ini, поэтому мы используем его.
#!/bin/bash
eval $(python3 << EOP
from configparser import SafeConfigParser
config = SafeConfigParser()
config.read("config.ini"))
for section in config.sections():
for (key, val) in config.items(section):
print(section + "_" + key + "=\"" + val + "\"")
EOP
)
echo "Environment_type: ${Environment_type}"
echo "Environment_name: ${Environment_name}"
config.ini
[Environment]
type = DEV
name = D01
Ответ "Карен Габриелян" среди других ответов был лучшим, но в некоторых средах у нас нет awk, как в типичном busybox, я изменил ответ с помощью кода ниже.
trim()
{
local trimmed="$1"
# Strip leading space.
trimmed="${trimmed## }"
# Strip trailing space.
trimmed="${trimmed%% }"
echo "$trimmed"
}
function parseIniFile() { #accepts the name of the file to parse as argument ($1)
#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 < $1
}
Это использует системный Perl и чистые регулярные выражения:
cat parameters.ini | perl -0777ne 'print "$1" if /\[\s*parameters\.ini\s*\][\s\S]*?\sdatabase_version\s*=\s*(.*)/'
сложная простота
ini файл
test.ini
[section1]
name1=value1
name2=value2
[section2]
name1=value_1
name2 = value_2
bash script с чтением и выполнением
/bin/parseini
#!/bin/bash
set +a
while read p; do
reSec='^\[(.*)\]$'
#reNV='[ ]*([^ ]*)+[ ]*=(.*)' #Remove only spaces around name
reNV='[ ]*([^ ]*)+[ ]*=[ ]*(.*)' #Remove spaces around name and spaces before value
if [[ $p =~ $reSec ]]; then
section=${BASH_REMATCH[1]}
elif [[ $p =~ $reNV ]]; then
sNm=${section}_${BASH_REMATCH[1]}
sVa=${BASH_REMATCH[2]}
set -a
eval "$(echo "$sNm"=\""$sVa"\")"
set +a
fi
done < $1
а затем в другом script я исхожу из результатов команды и могу использовать любые переменные внутри
test.sh
#!/bin/bash
source parseini test.ini
echo $section2_name2
Наконец, из командной строки вывод таким образом
# ./test.sh
value_2
Я написал быстрый и простой скрипт на 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
Это предполагает:
Надеюсь, это поможет: ¬)
В этой реализации используется awk
и имеет следующие преимущества:
;
Отформатированная версия:
awk -F '=' '/^\s*database_version\s*=/ {
sub(/^ +/, "", $2);
sub(/ +$/, "", $2);
print $2;
exit;
}' parameters.ini
Один лайнер
awk -F '=' '/^\s*database_version\s*=/ { sub(/^ +/, "", $2); sub(/ +$/, "", $2); print $2; exit; }' parameters.ini
Только что закончил писать собственный парсер. Я попытался использовать различный парсер, найденный здесь, но, похоже, не работает с ksh93 (AIX) и bash (Linux).
Старый стиль программирования - синтаксический разбор строки за строкой. Довольно быстро, так как он использовал несколько внешних команд. Немного медленнее из-за всех требований eval, необходимых для динамического имени массива.
Ini поддерживает 3 специальных синтаксиса:
Я использовал весь синтаксис thoses, чтобы иметь довольно сложный, повторно используемый ini файл. Полезно устанавливать продукты при установке новой ОС - мы делаем это много.
Доступ к значениям можно получить с помощью ${ini [$ section. $item]}. Массив ДОЛЖЕН быть определен перед вызовом этого.
Получайте удовольствие. Надеюсь, это полезно для кого-то еще!
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 "$0($@)"
typeset ini_file="$1"
# Name of the array to fill. By default, it "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 $0 [$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 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 $1,
# 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 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 $1,
# 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 $0($@)\n"
}
Когда я использую пароль в base64, я помещаю разделитель ":", потому что строка base64 может иметь "=". Например (я использую ksh
):
> echo "Abc123" | base64
QWJjMTIzCg==
В parameters.ini
поместите строку pass:QWJjMTIzCg==
и, наконец:
> PASS=`awk -F":" '/pass/ {print $2 }' parameters.ini | base64 --decode`
> echo "$PASS"
Abc123
Если строка имеет такие пробелы, как "pass : QWJjMTIzCg== "
добавить | tr -d ' '
, чтобы обрезать их:
> PASS=`awk -F":" '/pass/ {print $2 }' parameters.ini | tr -d ' ' | base64 --decode`
> echo "[$PASS]"
[Abc123]
Моя версия однострочного
#!/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=$1
SECTION=$2
ITEM=$3
cat $INIFILE | sed -n /^\[$SECTION\]/,/^\[.*\]/p | grep "^[:space:]*$ITEM[:space:]*=" | sed s/.*=[:space:]*//