Jak pobrać wartość INI w skrypcie powłoki?


102

Mam plik parameters.ini, taki jak:

[parameters.ini]
    database_user    = user
    database_version = 20110611142248

Chcę wczytać i użyć wersji bazy danych określonej w pliku parameters.ini z poziomu skryptu powłoki bash, aby móc ją przetworzyć.

#!/bin/sh    
# Need to get database version from parameters.ini file to use in script    
php app/console doctrine:migrations:migrate $DATABASE_VERSION

Jak bym to zrobił?


3
Czy którakolwiek z tych odpowiedzi w ogóle dotyczy sekcji?
ManuelSchneid3r

Odpowiedzi:


85

Co powiesz na grepowanie dla tej linii, a następnie użycie awk

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

6
Uwzględni to spacje po znaku „=”.

11
Aby przyciąć spacje, dodaj | tr -d ' 'na końcu.
kenorb

23
To naprawdę nie jest dobre rozwiązanie. Pomyśl o dwóch sekcjach [parameters.ini], z których każda ma zmienną „database_version”. Otrzymasz wtedy wartość dwukrotnie.
nerdoc

4
tak, proszę wziąć pod uwagę wyspecjalizowany parser ini, taki jak crudini, ponieważ istnieje wiele przypadków brzegowych
nieobsługiwanych

3
Nadal przydatne i szybsze w przypadku podstawowych plików ini.
Cyril N.

57

Możesz użyć natywnego analizatora składni bash do interpretacji wartości ini, wykonując:

$ source <(grep = file.ini)

Przykładowy plik:

[section-a]
  var1=value1
  var2=value2
  IPS=( "1.2.3.4" "1.2.3.5" )

Dostępu do zmiennych, po prostu ich drukowania: echo $var1. Możesz także używać tablic, jak pokazano powyżej ( echo ${IPS[@]}).

Jeśli chcesz tylko jedną wartość, po prostu grep dla niej:

source <(grep var1 file.ini)

Aby zobaczyć wersję demonstracyjną, sprawdź to nagranie na asciinema .

Jest to proste, ponieważ nie potrzebujesz żadnej zewnętrznej biblioteki do analizowania danych, ale ma pewne wady. Na przykład:

  • Jeśli masz spacje między =(nazwa i wartość zmiennej), to najpierw musisz skrócić spacje, np

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

    Lub jeśli nie dbasz o spacje (w tym w środku), użyj:

      $ source <(grep = file.ini | tr -d ' ')
    
  • Aby wspierać ;komentarze, zamień je na #:

      $ sed "s/;/#/g" foo.ini | source /dev/stdin
    
  • Sekcje nie są obsługiwane (np. Jeśli masz [section-name], musisz je odfiltrować, jak pokazano powyżej, np. grep =), To samo dla innych nieoczekiwanych błędów.

    Jeśli trzeba przeczytać konkretną wartość w określonej sekcji, wykorzystanie grep -A, sed, awklub ex).

    Na przykład

      source <(grep = <(grep -A5 '\[section-b\]' file.ini))
    

    Uwaga: gdzie -A5jest liczba wierszy do przeczytania w sekcji. Wymień sourcesię catdo debugowania.

  • Jeśli masz jakieś błędy analizy, zignoruj ​​je, dodając: 2>/dev/null

Zobacz też:


1
ale ... source <(grep = <(grep -A5 '\[section-b\]' file.ini))to nie zadziała: [sec a] a = 1 b = 2 c = 3 [sec b] a = 2 b = 3 [sec c] a = 0. gdzie nie ma określonej reguły z liniami
Psychozoic

Próbowałem użyć źródła, ale kiedy powtarzam $ var1, nic nie zwraca. Czemu?
A. Gh

@ A.Gh Nie jestem pewien, działa dla mnie. Upewnij się, że używasz powłoki Bash. Zobacz: asciinema.org/a/306481
kenorb

Byłoby to eleganckie, ale nie udało się go uruchomić w systemie OS X (Catalina). Działa z wiersza poleceń w zsh (bieżąca domyślna powłoka), ale po umieszczeniu go w skrypcie pojawia się błąd syntax error near unexpected token '('. W przypadku basha po cichu zawodzi zarówno w przypadku zachęty, jak i skryptu.
MiRin

30

Bash nie zapewnia parsera dla tych plików. Oczywiście możesz użyć polecenia awk lub kilku wywołań seda, ale jeśli jesteś bash-kapłanem i nie chcesz używać żadnej innej powłoki, możesz wypróbować następujący niejasny kod:

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

Stosowanie:

# 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 można znaleźć na blogu The Old School DevOps .


3
Chociaż ten link może odpowiedzieć na pytanie, lepiej jest zawrzeć tutaj zasadnicze części odpowiedzi i podać link do odniesienia. Odpowiedzi zawierające tylko łącze mogą stać się nieprawidłowe, jeśli połączona strona ulegnie zmianie.
alecxe

8
Zwykle to ja udzielam takich komentarzy; mogę tylko powiedzieć, że byłem młody i głupi :-)
Fredrik Pihl

1
Jeśli podoba Ci się ten fragment, na github.com/albfan/bash-ini-parser jest ulepszenie
albfan

3
Aby działać poprawnie, należy użyć cfg_parser zamiast cfg.parser
Wes,

1
TYPO: „cfg.parser” powinno być „cfg_parser”.
Setop

27

Jednowarstwowa Sed, uwzględniająca sekcje. Przykładowy plik:

[section1]
param1=123
param2=345
param3=678

[section2]
param1=abc
param2=def
param3=ghi

[section3]
param1=000
param2=111
param3=222

Powiedz, że chcesz param2 z sekcji 2. Uruchom następujące:

sed -nr "/^\[section2\]/ { :l /^param2[ ]*=/ { s/.*=[ ]*//; p; q;}; n; b l;}" ./file.ini

da tobie

def

3
sed -nr "/ ^ \ [SECTION2 \] / {: l /^\s*[^#].*/ p; n; / ^ \ [/ q; bl;}" file.conf #, aby pobrać całą sekcję bez komentarzy dla pliku stylu .conf z [SECTION2] i # liniami komentarzy w stylu hash. Następnie grep dla paramname, jeśli chcesz tylko jeden parametr.
gaoithe

lepiej używać adresów z zakresu seda niż czytać następne wiersze:"/^\[section2\]/,/^\[/{...}"
basin

1
Jeżeli na Mac: brew install gnu-sedi następnie za pomocą gsed(inaczej: sed: illegal option -- r)
frnhr

Czy ktoś może wyjaśnić, jak sed -nr "/^\[SECTION2\]/ { :l /^\s*[^#].*/ p; n; /^\[/ q; b l; }" działa to wyrażenie? dziękuję
foo_l

21

Po prostu dołącz swój plik .ini do treści bash:

Plik example.ini :

DBNAME=test
DBUSER=scott
DBPASSWORD=tiger

Plik przykład.sh

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

3
To powinna być wybrana odpowiedź. Działa z file.properties i jest odporny na błędy (plik z pustą linią w środku). Dzięki
Anthony,

21
nie obsługuje części [sekcja] plików INI.
Setop

to najlepsza odpowiedź!
JavaSheriff

17
Miejmy nadzieję, że nikt nigdy nie doda „rm -rf /” do pliku ini :(
HeyMan

1
Znacznie bezpieczniejsze w podpowłoce: $ (. Example.ini; echo $ DBNAME)
Rich Remer

14

Wszystkie rozwiązania, które do tej pory widziałem, trafiały również do zakomentowanych linii. Ten nie, jeśli kod komentarza to ;:

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

2
Powinno to być odpowiedź przyjęte, ponieważ a) obsługuje wykomentowane linia B) proste :)
Sudar

1
To jest wspaniałe, ty @PenguinLust! Sposób użycia: 1. Pełnowierszowe komentarze dozwolone z prefiksem średnika (niedozwolone są wbudowane komentarze końca wiersza); 2. Spacja nie jest usuwana z wyniku (więc jeśli plik ini ma „a = 1”, to wyszukiwanie „a” w skrypcie daje „1”).
AnneTheAgile

1
Aby przyciąć spacje, dodaj | tr -d ' 'na końcu.
kenorb

To ma ten sam problem, co sugerowana odpowiedź; wyszukuje każdą instancję „database_version”
Nubcake

12

jedno z wielu możliwych rozwiązań

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

8

Wyświetlanie wartości my_key w ini stylu my_file :

sed -n -e 's/^\s*my_key\s*=\s*//p' my_file
  • -n - domyślnie niczego nie drukuj
  • -e - wykonaj wyrażenie
  • s/PATTERN//p - wyświetl wszystko, co jest zgodne z tym wzorem we wzorze:
  • ^ - wzór zaczyna się na początku linii
  • \s - biały znak
  • * - zero lub wiele (białe znaki)

Przykład:

$ 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

Więc:

Znajdź wzorzec, w którym linia zaczyna się od zera lub wielu białych znaków, po których następuje ciąg my_key , po którym następuje zero lub wiele białych znaków, znak równości, a następnie zero lub wiele białych znaków. Wyświetl resztę treści w tym wierszu po tym wzorcu.


Twój przykład nie działa (nie jest barwydrukowany), przynajmniej na Unix / OSX.
kenorb

7

Podobnie jak w przypadku innych odpowiedzi Pythona, możesz to zrobić za pomocą -cflagi, aby wykonać sekwencję instrukcji Pythona podaną w wierszu poleceń:

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

Ma to tę zaletę, że wymaga tylko standardowej biblioteki Python i ma tę zaletę, że nie wymaga pisania oddzielnego pliku skryptu.

Lub użyj dokumentu tutaj dla lepszej czytelności, a zatem:

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

Co jeśli chcę pobrać całą sekcję jako tablicę za pomocą tego polecenia?
Debopam Parua

7

sed

Możesz użyć seddo przeanalizowania pliku konfiguracyjnego ini, szczególnie jeśli masz nazwy sekcji, takie jak:

# 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

więc możesz użyć następującego sedskryptu do przeanalizowania powyższych danych:

# 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
}

spowoduje to konwersję danych ini do tego płaskiego formatu:

owner|name|John Doe
owner|organization|Acme Widgets Inc.
database|server|192.0.2.62
database|port|143
database|file|payroll.dat

więc będzie łatwiej analizować za pomocą sed, awklub readpoprzez rozdział imion w każdej linii.

Podziękowania i źródło: Pliki konfiguracyjne dla skryptów powłoki , Michael Grünewald


Alternatywnie możesz użyć tego projektu chilladx/config-parser:, parsera konfiguracji używającego sed.


To jest świetne! Myślałem o spłaszczeniu tego w ten sposób, ale to jest o wiele więcej niż to, co zamierzałem razem zhakować!
grinch

6

Możesz użyć crudininarzędzia, aby uzyskać wartości ini, np .:

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

Zauważ, że jest oparty na Pythonie, więc może nie być odpowiedni dla np. Wbudowanych aplikacji Linux.
Craig McQueen

Jest to część standardowych repozytoriów Fedory (testowane z 31). yum install crudini
złośnica

5

Dla ludzi (takich jak ja), którzy chcą czytać pliki INI ze skryptów powłoki (czytaj powłokę, a nie bash) - uruchomiłem małą bibliotekę pomocniczą, która próbuje dokładnie to zrobić:

https://github.com/wallyhall/shini (licencja MIT, zrób z nią, jak chcesz. Podałem powyżej link, w tym w treści, ponieważ kod jest dość długi).

To jest trochę bardziej „skomplikowane” niż proste sed sugerowane powyżej linie - ale działa na bardzo podobnej podstawie.

Funkcja wczytuje plik wiersz po wierszu - wyszukuje znaczniki sekcji ( [section]) i deklaracje klucza / wartości (key=value ).

Ostatecznie otrzymasz wywołanie zwrotne do własnej funkcji - sekcji, klucza i wartości.


@CraigMcQueen - dodałem dziś wieczorem obsługę zapisu o bardzo jakości alfa. Nie jest to „kompletne” na żadnym odcinku wyobraźni!
wally

Znakomity! :-) Major
Jonathan

2

Niektóre odpowiedzi nie uwzględniają komentarzy. Niektórzy nie szanują sekcji. Niektórzy rozpoznają tylko jedną składnię (tylko „:” lub tylko „=”). Niektóre odpowiedzi w języku Python kończą się niepowodzeniem na moim komputerze z powodu różnych kodowań lub niepowodzenia zaimportowania modułu sys. Wszystkie są dla mnie zbyt lakoniczne.

Napisałem więc swój własny, a jeśli masz nowoczesny Python, prawdopodobnie możesz to wywołać z powłoki Bash. Ma tę zaletę, że jest zgodny z niektórymi powszechnymi konwencjami kodowania w Pythonie, a nawet zapewnia rozsądne komunikaty o błędach i pomoc. Aby go użyć, nazwij go czymś w rodzaju myconfig.py (NIE nazywaj go configparser.py lub może próbować zaimportować się), uczyń go wykonywalnym i nazwij go jak

value=$(myconfig.py something.ini sectionname value)

Oto mój kod dla Pythona 3.5 w systemie 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

złożona prostota

ini plik

test.ini

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

bash z odczytem i wykonaniem

/ 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

następnie w innym skrypcie pobieram wyniki polecenia i mogę użyć dowolnych zmiennych w nim zawartych

test.sh

#!/bin/bash

source parseini test.ini

echo $section2_name2

w końcu z wiersza poleceń wynik jest następujący

# ./test.sh 
value_2

Świetne rozwiązanie! Dzięki!
Michael

2

Oto moja wersja, która analizuje sekcje i zapełnia nią globalną tablicę asocjacyjną g_iniProperties . Zauważ, że działa to tylko z bash v4.2 i nowszymi.

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
}

A oto przykładowy kod wykorzystujący powyższą funkcję:

parseIniFile "/path/to/myFile.ini"
for key in "${!g_iniProperties[@]}"; do
    echo "Found key/value $key = ${g_iniProperties[$key]}"
done

1

Ten skrypt otrzyma następujące parametry:

co oznacza, że ​​jeśli twój ini ma:

pars_ini.ksh <ścieżka do pliku ini> <nazwa sektora w pliku Ini> <nazwa w nazwie = wartość do zwrócenia>

na przykład. jak to nazwać:


[środowisko]

a = x

[DataBase_Sector]

DSN = coś


Następnie dzwoni:

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

to spowoduje pobranie następującego „czegoś”

skrypt „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

Napisałem szybki i łatwy skrypt w Pythonie do włączenia do mojego skryptu bash.

Na przykład, twój plik ini jest wywoływany food.ini iw pliku możesz mieć kilka sekcji i kilka linii:

[FRUIT]
Oranges = 14
Apples = 6

Skopiuj ten mały 6-wierszowy skrypt Pythona i zapisz go jako 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])

Teraz możesz to zrobić na przykład w swoim skrypcie bash.

OrangeQty=$(python configparser.py food.ini FRUIT Oranges)

lub

ApplesQty=$(python configparser.py food.ini FRUIT Apples)
echo $ApplesQty

To zakłada:

  1. masz zainstalowany Python
  2. masz zainstalowaną bibliotekę configparser (powinna być dołączona do standardowej instalacji Pythona)

Mam nadzieję, że to pomoże : ¬)


Szukałem czegoś, co robiło to, więc poszedłem za przykładem i działa dobrze. Zapomniałem, że to napisałem !!!! Próbowałem głosować na siebie, ale niestety nie mogę głosować na siebie !!! ha ha
joe_evans

0

Moja wersja jednowierszowego

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

Właśnie skończyłem pisać własny parser. Próbowałem użyć różnych parserów znalezionych tutaj, żaden nie działa zarówno z ksh93 (AIX), jak i bash (Linux).

To stary styl programowania - parsowanie linia po linii. Dość szybko, ponieważ używał kilku poleceń zewnętrznych. Nieco wolniej ze względu na wszystkie eval wymagane dla dynamicznej nazwy tablicy.

Ini obsługuje 3 specjalne składnie:

  • includeefile = ini file -> Załaduj dodatkowy plik ini. Przydatne do dzielenia ini na wiele plików lub ponownego wykorzystania jakiejś części konfiguracji
  • includedir = directory -> To samo, co includeefile, ale zawiera pełny katalog
  • obejmuje sekcję = sekcja -> Skopiuj istniejącą sekcję do bieżącej sekcji.

Użyłem całej tej składni, aby uzyskać dość złożony, wielokrotnego użytku plik ini. Przydatne do instalowania produktów podczas instalowania nowego systemu operacyjnego - robimy to często.

Dostęp do wartości można uzyskać za pomocą $ {ini [$ section. $ Item]}. Tablica MUSI zostać zdefiniowana przed wywołaniem tego.

Baw się dobrze. Mam nadzieję, że będzie to przydatne dla kogoś innego!

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

Ta implementacja wykorzystuje awki ma następujące zalety:

  1. Zwróci tylko pierwszy pasujący wpis
  2. Ignoruje wiersze zaczynające się od ;
  3. Przycina początkowe i końcowe białe znaki, ale nie wewnętrzne białe znaki

Wersja sformatowana :

awk -F '=' '/^\s*database_version\s*=/ {
            sub(/^ +/, "", $2);
            sub(/ +$/, "", $2);
            print $2;
            exit;
          }' parameters.ini

Jedna linijka :

awk -F '=' '/^\s*database_version\s*=/ { sub(/^ +/, "", $2); sub(/ +$/, "", $2); print $2; exit; }' parameters.ini

0

Kiedy używam hasła w base64, wstawiam separator „:”, ponieważ ciąg base64 może mieć „=”. Na przykład (używam ksh):

> echo "Abc123" | base64
QWJjMTIzCg==

W parameters.iniumieścić linię pass:QWJjMTIzCg==, a na końcu:

> PASS=`awk -F":" '/pass/ {print $2 }' parameters.ini | base64 --decode`
> echo "$PASS"
Abc123

Jeśli linia ma spacje, takie jak "pass : QWJjMTIzCg== "dodaj, | tr -d ' 'aby je przyciąć:

> PASS=`awk -F":" '/pass/ {print $2 }' parameters.ini | tr -d ' ' | base64 --decode`
> echo "[$PASS]"
[Abc123]

0

Wykorzystuje systemowy perl i czyste wyrażenia regularne:

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

0

Odpowiedź „Karen Gabrielyan” spośród innych odpowiedzi była najlepsza, ale w niektórych środowiskach, w których nie mamy awk, jak typowy busybox, zmieniłem odpowiedź za pomocą poniższego kodu.

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
    }

Nie jestem do końca pewien, jakie jest prawdopodobieństwo, że brakuje awk, ale dostępne są sed, cut i stosunkowo bardziej zaawansowana składnia basha.
Ondrej K.

Większość początkowych systemów plików root implementuje / linuxrc lub / init jako skrypt powłoki, a tym samym zawiera minimalną powłokę (zwykle / bin / ash) wraz z kilkoma niezbędnymi narzędziami przestrzeni użytkownika
Ehsan Ahmadi

Pewnie. Jestem trochę zaskoczony, że zbudowałeś swój busybox bez awk, ale wciąż z sed, cut i obsługą różnych "bashizmów". Nie żeby to nie było możliwe, po prostu zastanawiam się. ;)
Ondrej K.

Inne narzędzia są lżejsze niż awk. Jeśli napiszesz skrypt w initramfs za pomocą initramfs-tools w dystrybucji ubuntu, okaże się, że nie masz awk, a także inne narzędzia, takie jak sed, grep ... działają w minimalnym stopniu.
Ehsan Ahmadi

Jasne, nie mówię o GNU awk lub innym pełnym awk, po prostu zastanawiam się, ile można zaoszczędzić konfigurując busybox tak, aby nie zawierał obsługi awk (szczególnie biorąc pod uwagę, że inne wspomniane bity nie są usuwane z tej konfiguracji). Możliwe, że * buntu initrd ma taki właśnie. Zastanawiam się tylko nad kombinacją / wyborem, to wszystko.
Ondrej K.

0

Jeśli Python jest dostępny, poniższe odczytują wszystkie sekcje, klucze i wartości i zapisują je w zmiennych z ich nazwami w formacie „[sekcja] _ [klucz]”. Python potrafi poprawnie czytać pliki .ini, więc korzystamy z tego.

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

Możesz użyć parsera CSV xsv do analizowania danych 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

lub z pliku.

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

0

Jeśli używasz sekcji, to wystarczy:

Przykład nieprzetworzonego wyniku:

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

Analiza 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'

Teraz wszyscy razem:

$ 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

Mam ładną jedną linijkę (założyć, że masz phpi jqzainstalowałem):

cat file.ini | php -r "echo json_encode(parse_ini_string(file_get_contents('php://stdin'), true, INI_SCANNER_RAW));" | jq '.section.key'
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.