Як отримати значення INI у сценарії оболонки?


97

У мене є файл 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

Відповіді:


83

Як щодо grepping для цього рядка, а потім використовувати awk

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

6
Це буде включати пробіли після '='.

10
Щоб обрізати пробіли, додайте | tr -d ' 'в кінці.
kenorb

22
Це насправді не є гарним рішенням. Подумайте, що у вас є 2 розділи [parameters.ini], кожен із яких має змінну 'database_version'. Тоді ви отримуєте значення двічі.
nerdoc

4
так, будь ласка, розгляньте спеціалізований синтаксичний синтаксичний аналізатор, такий як crudini, оскільки є безліч крайових випадків, які не розглядаються вище
pixelbeat

3
Все-таки корисно та швидше для основних файлів ini.
Кирило Н.

51

Для інтерпретації значень ini ви можете використовувати натиснуту аналізатор bash для:

$ 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. де немає чіткого правила з лініями
Психозой

Я намагався використати джерело, але коли я повторюю $ var1, це нічого не повертає. Чому?
A. Gh

@ A.Gh Я не впевнений, працює для мене. Переконайтеся, що ви використовуєте оболонку Bash. Див .: asciinema.org/a/306481
kenorb

Це було б елегантно, але не вдалося змусити його працювати в OS X (Каталіна). Він працює з командного рядка в zsh (поточна оболонка за замовчуванням), але як тільки я поміщу його в сценарій, я отримую помилку syntax error near unexpected token '('. З bash, він мовчки не виходить як з підказки, так і з сценарію.
MiRin

29

Bash не надає парсер для цих файлів. Очевидно, що ви можете використовувати команду awk або пару дзвінків sed, але якщо ви bash-жрец і не хочете використовувати будь-яку іншу оболонку, тоді ви можете спробувати такий незрозумілий код:

#!/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"

Bash ini-аналізатор можна знайти на веб-сайті The Old School DevOps .


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

8
Зазвичай я даю такі коментарі; все, що я можу сказати, це те, що я був молодим і дурним :-)
Фредрік Піл,

1
Якщо вам подобається цей фрагмент, на github.com/albfan/bash-ini-parser
albfan

3
Для коректної роботи потрібно використовувати cfg_parser замість cfg.parser
Вес

1
ТИПО: "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 "/ ^ \ [РОЗДІЛ2 \] / {: l /^\s*[^#].*/ p; n; / ^ \ [/ q; bl;}" file.conf #, щоб отримати цілий розділ без коментарів до файлу стилю .conf із [РОЗДІЛОМ2] та # рядками коментарів у стилі хеш. Тоді grep для paramname, якщо вам потрібен лише один параметр.
gooithe

краще використовувати адреси діапазону sed, ніж читати наступні рядки:"/^\[section2\]/,/^\[/{...}"
басейн

1
якщо на макінтош: brew install gnu-sedа потім використовувати gsed(інакше: sed: illegal option -- r)
frnhr

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

22

Просто включіть свій файл .ini у тіло bash:

Файл example.ini :

DBNAME=test
DBUSER=scott
DBPASSWORD=tiger

Файл example.sh

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

2
Це має бути обрана відповідь. Він працює з file.properties і є стійким до несправностей (файл із порожнім рядком всередині). Дякую
Ентоні

17
не обробляє [розділ] частини файлів INI.
Сетоп

це найкраща відповідь!
JavaSheriff

17
Сподіваємось, ніхто ніколи не додає "rm -rf /" до файлу ini :(
HeyMan

1
Набагато безпечніше в суб-оболонці: $ (. Example.ini, відлуння $ DBNAME)
Rich Ремер

14

Всі рішення, які я бачив до цього часу, також вражають коментарі. Цей не зробив, якщо код коментаря ;:

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

2
Це повинна бути прийнята відповідь, оскільки а) Він обробляє прокоментовані рядки б) простий :)
Судар

1
Це приголомшливо, ти @PenguinLust! Використання: 1. Повний коментар дозволений з точкою крапки з комою (заборонені коментарі до кінця рядка); 2.Whitespace не видаляється з результату (тому, якщо у файлі 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
землерийка

5

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

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

Це дещо «складніше», ніж прості sedрядки, запропоновані вище, - але працює на дуже подібній основі.

Функція читає у файлі по черзі - шукає маркери розділів ( [section]) та декларації ключа / значення ( key=value).

Зрештою, ви отримуєте зворотний виклик для власної функції - розділу, ключа та значення.


@CraigMcQueen - Я сьогодні додав підтримку написання дуже альфа-якості. Це не "завершено" будь-яким проміжком уяви!
Уоллі

Блискуче! :-) Майор
Джонатан

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

Що робити, якщо я хочу захопити цілий розділ як Array за допомогою цієї команди?
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

Чудове рішення! Дякую!
Майкл

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> <ім'я в name = значення для повернення>

напр. як це назвати:


[ середовище ]

a = x

[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. у вас встановлена ​​бібліотека конфігураторів (це має бути встановлено 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 файл -> Завантажити додатковий ini файл. Корисно для розбиття ini на кілька файлів або повторного використання якоїсь частини конфігурації
  • includedir = directory -> Те саме, що includefile, але включає повний каталог
  • includesection = section -> Скопіюйте наявний розділ до поточного розділу.

Я використовував усі синтаксиси 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'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

Відповідь "Карен Габриелян" серед інших відповідей була найкращою, але в деяких середовищах у нас не буває дивності, як типовий зайнятий ящик, я змінив відповідь нижче за кодом.

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 як синтаксис.
Ondrej K.

Більшість початкових кореневих файлових систем реалізують / linuxrc або / init як скрипт оболонки і, таким чином, включають мінімальну оболонку (зазвичай / bin / ash) разом із деякими важливими утилітами простору користувача
Ehsan Ahmadi

Звичайно. Я лише трохи здивований, що ви створили свій зайнятий boxbox без awk, але все ж із sed, cut та підтримкою різних "башизмів". Не те, що це було б неможливо, просто змушує мене дивуватися. ;)
Ондрей К.

Інші інструменти - це легкіші, ніж awk. якщо ви напишете скрипт в initramfs за допомогою інструментів initramfs в дистрибутиві ubuntu, ви виявите, що у вас немає awk, а також інші інструменти, такі як sed, grep ..., працюють мінімально.
Ехсан Ахмаді

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

0

Якщо Python доступний, наступне прочитає всі розділи, ключі та значення та збереже їх у змінних з їх іменами у форматі "[section] _ [key]". 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

Аналіз Regexp:

$ ./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.