Как мне получить значение INI в сценарии оболочки?


100

У меня есть файл parameters.ini, например:

[parameters.ini]
    database_user    = user
    database_version = 20110611142248

Я хочу прочитать и использовать версию базы данных, указанную в файле parameters.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

Как мне это сделать?


2
Уважает ли какой-либо из этих ответов разделы?
ManuelSchneid3r

Ответы:


85

Как насчет grepping для этой строки, а затем с помощью awk

version=$(awk -F "=" '/database_version/ {print $2}' parameters.ini)

6
Это будет включать пробелы после '='.

11
Чтобы обрезать пробелы, добавьте | tr -d ' 'в конце.
kenorb

23
Это не совсем хорошее решение. Представьте, что у вас есть 2 раздела [parameters.ini], в каждом из которых есть переменная database_version. Тогда вы получите значение дважды.
nerdoc

4
да, пожалуйста, подумайте о специализированном ini-парсере, таком как crudini, так как есть много крайних случаев, которые не обрабатываются вышеупомянутым
pixelbeat

3
По-прежнему полезно и быстрее для основных файлов ini.
Кирилл Н.

53

Вы можете использовать собственный парсер 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)

Для демонстрации посмотрите эту запись в asciinema .

Это просто, так как вам не нужна никакая внешняя библиотека для анализа данных, но у нее есть некоторые недостатки. Например:

  • Если у вас есть пробелы между =(именем переменной и значением), вы должны сначала обрезать пробелы, например

      $ source <(grep = file.ini | sed 's/ *= */=/g')

    Или, если вам не нужны пробелы (в том числе в середине), используйте:

      $ source <(grep = file.ini | tr -d ' ')
  • Чтобы поддержать ;комментарии, замените их на #:

      $ 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

Смотрите также:


1
но ... source <(grep = <(grep -A5 '\[section-b\]' file.ini))это не сработает для него: [sec a] a = 1 b = 2 c = 3 [sec b] a = 2 b = 3 [sec c] a = 0. где нет определенного правила с линиями
Psychozoic

Я пытался использовать источник, но когда я повторяю $ var1, он ничего не возвращает. Зачем?
A. Gh

@ A.Gh Не уверен, у меня работает. Убедитесь, что вы используете оболочку Bash. См .: asciinema.org/a/306481
kenorb

Это было бы элегантно, но не смогло заставить его работать в OS X (Catalina). Он работает из командной строки в zsh (текущая оболочка по умолчанию), но как только я помещаю его в скрипт, я получаю сообщение об ошибке syntax error near unexpected token '('. С bash он молча выходит из строя как из приглашения, так и из сценария.
MiRin

30

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 after =
    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"

INI-парсер Bash можно найти на сайте блога Old School DevOps .


3
Хотя эта ссылка может дать ответ на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если ссылка на страницу изменится.
alecxe

8
Обычно такие комментарии даю я; все, что я могу сказать, это то, что я был молод и глуп :-)
Фредрик Пил

1
Если вам понравился этот фрагмент, есть улучшение на github.com/albfan/bash-ini-parser
albfan 01

3
Для правильной работы необходимо использовать cfg_parser вместо cfg.parser
Уэс,

1
TYPO: cfg.parser должен быть cfg_parser.
Сетоп,

26

Sed однострочный, учитывающий сечения. Пример файла:

[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

3
sed -nr "/ ^ \ [SECTION2 \] / {: l /^\s*[^# provided.*/ p; n; / ^ \ [/ q; bl;}" file.conf # чтобы получить весь раздел без комментариев для файла стиля .conf с [SECTION2] и # строками комментариев в стиле хеша. Затем используйте команду grep для имени параметра, если вам нужен только один параметр.
gaoithe

более эффективное использование СЭД адресов диапазона , чем читать следующие строки:"/^\[section2\]/,/^\[/{...}"
таз

1
если на макинтош: brew install gnu-sedа затем использовать gsed(иначе: sed: illegal option -- r)
frnhr

Кто-нибудь может объяснить, как sed -nr "/^\[SECTION2\]/ { :l /^\s*[^#].*/ p; n; /^\[/ q; b l; }" работает выражение? спасибо
foo_l

21

Просто включите ваш .ini файл в тело bash:

Пример файла .ini :

DBNAME=test
DBUSER=scott
DBPASSWORD=tiger

Пример файла.sh

#!/bin/bash
#Including .ini file
. example.ini
#Test
echo "${DBNAME}   ${DBUSER}  ${DBPASSWORD}"

2
Это должен быть выбранный ответ. Он действительно работает с file.properties и является отказоустойчивым (файл с пустой строкой внутри). Спасибо
Энтони

18
не обрабатывает часть [section] INI-файлов.
Сетоп,

это лучший ответ!
JavaSheriff

17
Надеюсь, никто никогда не добавляет "rm -rf /" в ini файл :(
HeyMan

1
Намного безопаснее в суб-оболочке: $ (. Example.ini; echo $ DBNAME)
Рич Ремер

14

Все решения, которые я видел до сих пор, также попадают в закомментированные строки. Этот не сделал, если код комментария ;:

awk -F '=' '{if (! ($0 ~ /^;/) && $0 ~ /database_version/) print $2}' file.ini

2
Это должен быть принятый ответ, так как а) он обрабатывает закомментированные строки б) просто :)
Сударь

1
Это потрясающе, ты @PenguinLust! Использование: 1. Допускаются полнострочные комментарии с префиксом точки с запятой (строчные комментарии в конце строки не допускаются); 2. Пробелы не исключаются из результата (поэтому, если ini-файл имеет «a = 1», то поиск сценария для «a» оценивается как «1»).
AnneTheAgile

1
Чтобы обрезать пробелы, добавьте | tr -d ' 'в конце.
kenorb

Это та же проблема, что и предложенный ответ; он ищет все экземпляры «database_version»
Nubcake

12

одно из более возможных решений

dbver=$(sed -n 's/.*database_version *= *\([^ ]*.*\)/\1/p' < parameters.ini)
echo $dbver

8

Отобразите значение 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.
kenorb

7

sed

Вы можете использовать его 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/\[\(.*\)\]/\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имея названия разделов в каждой строке.

Источники и источник: файлы конфигурации для сценариев оболочки , Михаэль Грюневальд.


В качестве альтернативы вы можете использовать этот проект:, chilladx/config-parserанализатор конфигурации, использующий sed.


Это круто! Я думал вот так сгладить его, но это намного превосходит то, что я собирался собрать вместе!
гринч

6

Вы можете использовать crudiniинструмент для получения значений ini, например:

DATABASE_VERSION=$(crudini --get parameters.ini '' database_version)

Обратите внимание, что он основан на Python, поэтому может не подходить, например, для встроенных приложений Linux.
Крейг Маккуин,

Это часть стандартных репозиториев Fedora (проверено с 31). yum install crudini
shrewmouse

5

Для людей (вроде меня), которые хотят читать файлы INI из сценариев оболочки (читайте оболочку, а не bash) - я создал небольшую вспомогательную библиотеку, которая пытается сделать именно это:

https://github.com/wallyhall/shini (лицензия MIT, делайте с ней, как хотите. Я дал ссылку выше, включая ее встроенную, так как код довольно длинный.)

Это несколько более "сложно", чем простые sedстроки, предложенные выше, но работает на очень похожей основе.

Функция читает файл построчно - ищет маркеры разделов ( [section]) и объявления ключей / значений ( key=value).

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


@CraigMcQueen - Сегодня я добавил поддержку записи очень высокого качества. Это не «завершено» ни в коем случае!
Уолли

Гениально! :-) Major
Джонатан

5

Подобно другим ответам Python, вы можете сделать это, используя -cфлаг для выполнения последовательности операторов Python, заданных в командной строке:

$ python3 -c "import configparser; c = configparser.ConfigParser(); c.read('parameters.ini'); print(c['parameters.ini']['database_version'])"
20110611142248

Это имеет то преимущество, что для этого требуется только стандартная библиотека Python, а также преимущество отсутствия написания отдельного файла сценария.

Или используйте здесь документ для лучшей читаемости, таким образом:

#!/bin/bash
python << EOI
import configparser
c = configparser.ConfigParser()
c.read('params.txt')
print c['chassis']['serialNumber']
EOI

serialNumber=$(python << EOI
import configparser
c = configparser.ConfigParser()
c.read('params.txt')
print c['chassis']['serialNumber']
EOI
)

echo $serialNumber

Что, если я хочу получить с помощью этой команды целый раздел в виде массива?
Debopam Parua

2

Некоторые ответы не относятся к комментариям. Некоторые не уважают разделы. Некоторые распознают только один синтаксис (только «:» или только «=»). Некоторые ответы 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))

2

сложная простота

ini файл

test.ini

[section1]
name1=value1
name2=value2
[section2]
name1=value_1
  name2  =  value_2

bash скрипт с чтением и выполнением

/ 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

затем в другом скрипте я получаю результаты команды и могу использовать любые переменные внутри

test.sh

#!/bin/bash

source parseini test.ini

echo $section2_name2

наконец, из командной строки вывод таким образом

# ./test.sh 
value_2

Отличное решение! Спасибо!
Michael

2

Вот моя версия, которая анализирует разделы и заполняет ими глобальный ассоциативный массив 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

1

Этот скрипт получит следующие параметры:

это означает, что если в вашем ini есть:

pars_ini.ksh <путь к ini-файлу> <имя сектора в Ini-файле> <имя в имени = возвращаемое значение>

например. как это назвать:


[ Окружающая среда ]

а = х

[DataBase_Sector]

DSN = что-то


Затем звоню:

pars_ini.ksh /users/bubu_user/parameters.ini DataBase_Sector DSN

это приведет к получению следующего "чего-то"

скрипт "pars_ini.ksh":

\#!/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`

1

Я написал быстрый и простой скрипт Python, который нужно включить в свой скрипт bash.

Например, ваш ini-файл вызывается, food.ini и в нем могут быть несколько разделов и несколько строк:

[FRUIT]
Oranges = 14
Apples = 6

Скопируйте этот небольшой 6-строчный скрипт Python и сохраните его как configparser.py

#!/usr/bin/python
import configparser
import sys
config = configparser.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

Это предполагает:

  1. у вас установлен Python
  2. у вас установлена ​​библиотека configparser (она должна поставляться с установкой std python)

Надеюсь, это поможет : ¬)


Я искал что-то подобное, поэтому я последовал примеру, и он отлично работает. Я забыл, что написал это !!!! Я пытался проголосовать за себя, но, увы, не могу проголосовать за себя !!! ха-ха
joe_evans

0

Моя версия однострочника

#!/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:]*//

0

Только что закончил писать собственный парсер. Я пытался использовать различные парсеры, найденные здесь, похоже, ни один из них не работает как с ksh93 (AIX), так и с bash (Linux).

Это старый стиль программирования - построчный анализ. Довольно быстро, так как использовало мало внешних команд. Немного медленнее из-за того, что для динамического имени массива требуется eval.

Ini поддерживает 3 специальных синтаксиса:

  • includefile = ini file -> Загрузить дополнительный ini файл. Полезно для разделения ini на несколько файлов или повторного использования некоторой части конфигурации
  • includeir = directory -> То же, что includefile, но включает полный каталог
  • includesection = section -> Скопировать существующий раздел в текущий раздел.

Я использовал весь синтаксис, чтобы получить довольно сложный, повторно используемый 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'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 $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'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 $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'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 $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"
    }

0

Эта реализация использует awkи имеет следующие преимущества:

  1. Будет возвращена только первая совпадающая запись
  2. Игнорирует строки, начинающиеся с символа ;
  3. Обрезает начальные и конечные пробелы, но не внутренние пробелы

Форматированная версия :

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

0

Когда я использую пароль в 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]

0

Здесь используется системный Perl и чистые регулярные выражения:

cat parameters.ini | perl -0777ne 'print "$1" if /\[\s*parameters\.ini\s*\][\s\S]*?\sdatabase_version\s*=\s*(.*)/'

0

Ответ «Карен Габриелян» среди других ответов был лучшим, но в некоторых средах у нас нет 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
    }

Я не совсем уверен, насколько вероятно, что awk отсутствует, но доступны sed, cut и относительно более продвинутый синтаксис типа bash.
Ондрей К.

Большинство исходных корневых файловых систем реализуют / linuxrc или / init как сценарий оболочки и, таким образом, включают минимальную оболочку (обычно / bin / ash) вместе с некоторыми важными утилитами пользовательского пространства
Эхсан Ахмади

Конечно. Я просто немного удивлен, что вы создали свой busybox без awk, но все же с sed, cut и поддержкой различных "башизмов". Не то чтобы это было бы возможно, просто заставляет меня задуматься. ;)
Ондрей К.

Другие инструменты более легкие, чем awk. если вы напишете сценарий в initramfs с помощью initramfs-tools в дистрибутиве ubuntu, вы обнаружите, что у вас нет awk, а также другие инструменты, такие как sed, grep ..., работают минимально.
Эхсан Ахмади

Конечно, я не говорю о GNU awk или другом полномасштабном awk, просто интересно, сколько можно сэкономить, настроив busybox, чтобы не включать поддержку awk (особенно, учитывая, что другие упомянутые биты не удалены из этой конфигурации). Может быть, у * buntu initrd есть именно такой. Просто интересуюсь комбинацией / выбором, вот и все.
Ondrej K.

0

Если доступен 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

0

Вы можете использовать парсер CSV xsv для анализа INI-данных.

cargo install xsv
$ cat /etc/*release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
$ xsv select -d "=" - <<< "$( cat /etc/*release )" | xsv search --no-headers --select 1 "DISTRIB_CODENAME" | xsv select 2
xenial

или из файла.

$ xsv select -d "=" - file.ini | xsv search --no-headers --select 1 "DISTRIB_CODENAME" | xsv select 2

0

Если вы используете разделы, это выполнит свою работу:

Пример сырого вывода:

$ ./settings
[section]
SETTING_ONE=this is setting one
SETTING_TWO=This is the second setting
ANOTHER_SETTING=This is another setting

Разбор регулярного выражения:

$ ./settings | sed -n -E "/^\[.*\]/{s/\[(.*)\]/\1/;h;n;};/^[a-zA-Z]/{s/#.*//;G;s/([^ ]*) *= *(.*)\n(.*)/\3_\1='\2'/;p;}"
section_SETTING_ONE='this is setting one'
section_SETTING_TWO='This is the second setting'
section_ANOTHER_SETTING='This is another setting'

Теперь все вместе:

$ eval "$(./settings | sed -n -E "/^\[.*\]/{s/\[(.*)\]/\1/;h;n;};/^[a-zA-Z]/{s/#.*//;G;s/([^ ]*) *= *(.*)\n(.*)/\3_\1='\2'/;p;}")"
$ echo $section_SETTING_TWO
This is the second setting

0

У меня есть хороший однострочный (если у вас есть phpи jqустановлен):

cat file.ini | php -r "echo json_encode(parse_ini_string(file_get_contents('php://stdin'), true, INI_SCANNER_RAW));" | jq '.section.key'
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.