Wie greife ich einen INI-Wert in einem Shell-Skript ab?

96

Ich habe eine parameters.ini-Datei, wie zum Beispiel:

[parameters.ini]
    database_user    = user
    database_version = 20110611142248

Ich möchte die in der Datei parameters.ini angegebene Datenbankversion in einem Bash-Shell-Skript einlesen und verwenden, damit ich sie verarbeiten kann.

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

Wie würde ich das machen?

Stephen Watkins
quelle
2
Respektiert eine dieser Antworten überhaupt Abschnitte?
ManuelSchneid3r

Antworten:

83

Wie wäre es, nach dieser Zeile zu greifen und dann awk zu verwenden?

version=$(awk -F "=" '/database_version/ {print $2}' parameters.ini)
Ali Lown
quelle
6
Dies schließt Leerzeichen nach '=' ein.
10
Fügen Sie zum Trimmen von Leerzeichen | tr -d ' 'am Ende hinzu.
Kenorb
22
Dies ist keine wirklich gute Lösung. Stellen Sie sich 2 Abschnitte [parameters.ini] vor, von denen jeder eine Variable 'database_version' hat. Sie erhalten den Wert dann zweimal.
Nerdoc
4
Ja, bitte ziehen Sie einen spezialisierten Ini-Parser wie Crudini in Betracht, da es viele Randfälle gibt, die vom oben
genannten
3
Immer noch nützlich und schneller für einfache INI-Dateien.
Cyril N.
50

Sie können den nativen Bash-Parser verwenden, um INI-Werte zu interpretieren, indem Sie:

$ source <(grep = file.ini)

Beispieldatei:

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

Um auf Variablen zuzugreifen, drucken Sie sie einfach aus : echo $var1. Sie können auch Arrays wie oben gezeigt verwenden ( echo ${IPS[@]}).

Wenn Sie nur einen einzigen Wert möchten, suchen Sie einfach danach:

source <(grep var1 file.ini)

Überprüfen Sie für die Demo diese Aufnahme bei asciinema .

Es ist einfach, da Sie keine externe Bibliothek benötigen, um die Daten zu analysieren, aber es hat einige Nachteile. Beispielsweise:

  • Wenn Sie Leerzeichen zwischen =(Variablenname und -wert) haben, müssen Sie zuerst die Leerzeichen kürzen, z

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

    Oder wenn Sie sich nicht für die Leerzeichen interessieren (auch in der Mitte), verwenden Sie:

      $ source <(grep = file.ini | tr -d ' ')
  • Um ;Kommentare zu unterstützen , ersetzen Sie sie durch #:

      $ sed "s/;/#/g" foo.ini | source /dev/stdin
  • Die Abschnitte werden nicht unterstützt (z. B. wenn ja [section-name], müssen Sie sie wie oben gezeigt herausfiltern, z. B. grep =), ebenso für andere unerwartete Fehler.

    Wenn Sie bestimmten Wert unter bestimmten Abschnitt zu lesen, Verwendung grep -A, sed, awkoder ex).

    Z.B

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

    Hinweis: Wo -A5ist die Anzahl der Zeilen, die im Abschnitt gelesen werden sollen? Ersetzen Sie sourcemit catzu debuggen.

  • Wenn Sie Analysefehler haben, ignorieren Sie diese, indem Sie Folgendes hinzufügen: 2>/dev/null

Siehe auch:

Kenorb
quelle
1
aber ... source <(grep = <(grep -A5 '\[section-b\]' file.ini))das wird nicht funktionieren: [sec a] a = 1 b = 2 c = 3 [sec b] a = 2 b = 3 [sec c] a = 0. wo es keine bestimmte Regel mit Linien gibt
Psychozoic
Ich habe versucht, source zu verwenden, aber wenn ich $ var1 wiedergebe, wird nichts zurückgegeben. Warum?
A. Gh
@ A.Gh Ich bin nicht sicher, funktioniert für mich. Stellen Sie sicher, dass Sie die Bash-Shell verwenden. Siehe: asciinema.org/a/306481
Kenorb
29

Bash bietet keinen Parser für diese Dateien. Natürlich können Sie einen awk-Befehl oder ein paar sed-Aufrufe verwenden, aber wenn Sie Bash-Priester sind und keine andere Shell verwenden möchten, können Sie den folgenden obskuren Code ausprobieren:

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

Verwendung:

# 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 finden Sie auf der DevOps-Blog-Site von The Old School .

Fredrik Pihl
quelle
3
Während dieser Link die Frage beantworten kann, ist es besser, die wesentlichen Teile der Antwort hier aufzunehmen und den Link als Referenz bereitzustellen. Nur-Link-Antworten können ungültig werden, wenn sich die verknüpfte Seite ändert.
Alecxe
8
Normalerweise bin ich derjenige, der solche Kommentare abgibt. Alles was ich sagen kann ist, dass ich jung und dumm war :-)
Fredrik Pihl
1
Wenn Ihnen dieses Snippet gefällt, gibt es eine Erweiterung auf github.com/albfan/bash-ini-parser
albfan
3
Um richtig zu arbeiten, müssen Sie cfg_parser anstelle von cfg.parser
Wes
1
TYPO: "cfg.parser" sollte "cfg_parser" sein.
Setop
26

Sed Einzeiler, der Abschnitte berücksichtigt. Beispieldatei:

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

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

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

Angenommen, Sie möchten param2 aus Abschnitt2. Führen Sie Folgendes aus:

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

werde dir geben

def
egridasov
quelle
3
sed -nr "/ ^ \ [ABSCHNITT 2 \] / {: l /^\s*[^#‹.*/ p; n; / ^ \ [/ q; bl;}" file.conf #, um den gesamten Abschnitt abzurufen ohne Kommentare für eine .conf-Style-Datei mit [SECTION2] und # Hash-Style-Kommentarzeilen. Dann grep nach paramname, wenn Sie nur einen Parameter wollen.
Gaoithe
Verwenden Sie besser die Adressen des Sed-Bereichs als die nächsten Zeilen:"/^\[section2\]/,/^\[/{...}"
Becken
1
wenn auf einem Mac: brew install gnu-sedund dann verwenden gsed(sonst sed: illegal option -- r)
frnhr
Kann jemand bitte erklären, wie der sed -nr "/^\[SECTION2\]/ { :l /^\s*[^#].*/ p; n; /^\[/ q; b l; }" Ausdruck funktioniert? danke
foo_l
22

Fügen Sie einfach Ihre INI-Datei in den Bash-Body ein:

Datei example.ini :

DBNAME=test
DBUSER=scott
DBPASSWORD=tiger

Datei example.sh

#!/bin/bash
#Including .ini file
. example.ini
#Test
echo "${DBNAME}   ${DBUSER}  ${DBPASSWORD}"
Gustavo González
quelle
2
Dies sollte die ausgewählte Antwort sein. Es funktioniert mit file.properties und ist fehlertolerant (Datei mit leerer Zeile). Vielen Dank
Anthony
17
behandelt nicht den [Abschnitt] Teil von INI-Dateien.
Setop
Das ist die beste Antwort!
JavaSheriff
16
Hoffentlich fügt niemand jemals ein "rm -rf /" zur
INI-
1
Viel sicherer in der Sub-Shell: $ (. Example.ini; echo $ DBNAME)
Rich Remer
13

Alle Lösungen, die ich bisher gesehen habe, stießen auch auf auskommentierte Zeilen. Dieser hat es nicht getan, wenn der Kommentarcode lautet ;:

awk -F '=' '{if (! ($0 ~ /^;/) && $0 ~ /database_version/) print $2}' file.ini
Opux
quelle
2
Dies sollte die akzeptierte Antwort sein, da a) auskommentierte Zeilen behandelt werden b) einfach :)
Sudar
1
Das ist großartig, ty @PenguinLust! Verwendung: 1. Vollzeilige Kommentare mit Semikolon-Präfix zulässig (keine Inline-Zeilenende-Kommentare zulässig); 2.Whitespace wird nicht aus dem Ergebnis entfernt (wenn also die INI-Datei 'a = 1' hat, ergibt die Suche des Skripts nach 'a' '1').
AnneTheAgile
1
Fügen Sie zum Trimmen von Leerzeichen | tr -d ' 'am Ende hinzu.
Kenorb
Dies hat das gleiche Problem wie die vorgeschlagene Antwort. es sucht nach jeder Instanz von "database_version"
Nubcake
12

eine oder mehrere mögliche Lösungen

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

Zeigen Sie den Wert von my_key in einer my_file im INI -Stil an :

sed -n -e 's/^\s*my_key\s*=\s*//p' my_file
  • -n - Standardmäßig nichts drucken
  • -e - Führen Sie den Ausdruck aus
  • s/PATTERN//p - Zeigen Sie alles an, was diesem Muster folgt. Im Muster:
  • ^ - Muster beginnt am Zeilenanfang
  • \s - Leerzeichen
  • * - null oder viele (Leerzeichen)

Beispiel:

$ 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

So:

Suchen Sie ein Muster, bei dem die Zeile mit null oder vielen Leerzeichen beginnt, gefolgt von der Zeichenfolge my_key , gefolgt von null oder vielen Leerzeichen, einem Gleichheitszeichen und erneut null oder vielen Leerzeichen. Zeigen Sie den Rest des Inhalts in dieser Zeile nach diesem Muster an.

Dean Rather
quelle
Ihr Beispiel funktioniert nicht (nicht barausgedruckt), zumindest unter Unix / OSX.
Kenorb
7

sed

Sie können seddie INI-Konfigurationsdatei analysieren, insbesondere wenn Sie Abschnittsnamen wie:

# 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

Sie können also das folgende sedSkript verwenden, um die obigen Daten zu analysieren:

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

Dadurch werden die INI-Daten in dieses flache Format konvertiert:

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

so wird es einfacher sein , zu analysieren , verwenden sed, awkoder readindem sie in jedem Leitungsabschnitt Namen hat.

Credits & Quelle: Konfigurationsdateien für Shell-Skripte , Michael Grünewald


Alternativ können Sie dieses Projekt verwenden: chilladx/config-parsereinen Konfigurationsparser mit sed.

Kenorb
quelle
Das ist toll! Ich habe darüber nachgedacht, es so zu plattieren, aber das ist Meilen über das hinaus, was ich zusammen hacken wollte!
Grinch
6

Sie können das crudiniTool verwenden, um INI-Werte abzurufen, z.

DATABASE_VERSION=$(crudini --get parameters.ini '' database_version)
Pixelbeat
quelle
Beachten Sie, dass es auf Python basiert und daher möglicherweise nicht für eingebettete Linux-Anwendungen geeignet ist.
Craig McQueen
Dies ist Teil der Standard-Fedora-Repos (getestet mit 31). yum install crudini
Spitzmaus
5

Für Leute (wie mich), die INI-Dateien aus Shell-Skripten lesen möchten (Shell lesen, nicht Bash) - Ich habe die kleine Hilfsbibliothek aufgerissen, die genau das versucht:

https://github.com/wallyhall/shini (MIT-Lizenz, machen Sie damit, wie Sie möchten. Ich habe oben verlinkt und es inline eingefügt, da der Code ziemlich lang ist.)

Es ist etwas "komplizierter" als die sedoben vorgeschlagenen einfachen Zeilen - funktioniert aber auf einer sehr ähnlichen Basis.

Die Funktion liest eine Datei zeilenweise ein und sucht nach Abschnittsmarkierungen ( [section]) und Schlüssel- / Wertdeklarationen ( key=value).

Letztendlich erhalten Sie einen Rückruf zu Ihrer eigenen Funktion - Abschnitt, Schlüssel und Wert.

wally
quelle
@CraigMcQueen - Ich habe heute Abend Schreibunterstützung in Alpha-Qualität hinzugefügt. Es ist keineswegs "vollständig"!
Wally
Brillant! :-) Major
Jonathan
5

Ähnlich wie bei den anderen Python-Antworten können Sie dies mithilfe des -cFlags tun , um eine Folge von Python-Anweisungen auszuführen, die in der Befehlszeile angegeben werden:

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

Dies hat den Vorteil, dass nur die Python-Standardbibliothek erforderlich ist und keine separate Skriptdatei geschrieben wird.

Oder verwenden Sie ein Dokument hier, um die Lesbarkeit zu verbessern.

#!/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
Argentpepper
quelle
Was ist, wenn ich mit diesem Befehl einen ganzen Abschnitt als Array erfassen möchte?
Debopam Parua
2

Einige der Antworten respektieren keine Kommentare. Einige respektieren Abschnitte nicht. Einige erkennen nur eine Syntax (nur ":" oder nur "="). Einige Python-Antworten schlagen auf meinem Computer fehl, weil die Captialisierung unterschiedlich ist oder das sys-Modul nicht importiert werden kann. Alle sind mir etwas zu knapp.

Also habe ich mein eigenes geschrieben, und wenn Sie ein modernes Python haben, können Sie dies wahrscheinlich von Ihrer Bash-Shell aus aufrufen. Es hat den Vorteil, dass einige der gängigen Python-Codierungskonventionen eingehalten werden, und bietet sogar sinnvolle Fehlermeldungen und Hilfe. Um es zu verwenden, nennen Sie es so etwas wie myconfig.py (nennen Sie es NICHT configparser.py oder es könnte versuchen, sich selbst zu importieren), machen Sie es ausführbar und nennen Sie es wie

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

Hier ist mein Code für Python 3.5 unter 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))
4dummies
quelle
2

komplexe Einfachheit

INI-Datei

test.ini

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

Bash-Skript mit Lesen und Ausführen

/ 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

dann beziehe ich in einem anderen Skript die Ergebnisse des Befehls und kann beliebige Variablen darin verwenden

test.sh

#!/bin/bash

source parseini test.ini

echo $section2_name2

Schließlich ist über die Kommandozeile die Ausgabe somit

# ./test.sh 
value_2
Der faule Codierer
quelle
Tolle Lösung! Vielen Dank!
Michael
2

Hier ist meine Version, die Abschnitte analysiert und ein globales assoziatives Array g_iniProperties damit auffüllt . Beachten Sie, dass dies nur mit bash v4.2 und höher funktioniert.

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
}

Und hier ist ein Beispielcode mit der obigen Funktion:

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

Dieses Skript erhält folgende Parameter:

Das heißt, wenn Ihr INI hat:

pars_ini.ksh <Pfad zur INI-Datei> <Name des Sektors in der Ini-Datei> <Name in Name = zurückzugebender Wert>

z.B. wie man es nennt:


[ Umgebung ]

a = x

[DataBase_Sector]

DSN = etwas


Dann rufen Sie an:

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

Dadurch wird das folgende "Etwas" abgerufen.

das Skript "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`
jeo
quelle
1

Ich habe ein schnelles und einfaches Python-Skript geschrieben, das in mein Bash-Skript aufgenommen werden soll.

Zum Beispiel wird Ihre INI-Datei aufgerufen food.ini und in der Datei können Sie einige Abschnitte und einige Zeilen haben:

[FRUIT]
Oranges = 14
Apples = 6

Kopieren Sie dieses kleine 6-zeilige Python-Skript und speichern Sie es unter 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])

In Ihrem Bash-Skript können Sie dies beispielsweise tun.

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

oder

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

Dies setzt voraus:

  1. Sie haben Python installiert
  2. Sie haben die configparser-Bibliothek installiert (dies sollte mit einer Standard-Python-Installation geliefert werden).

Hoffe es hilft : ¬)

joe_evans
quelle
Ich habe nach etwas gesucht, das genau das getan hat, also bin ich dem Beispiel gefolgt und es funktioniert einwandfrei. Ich habe vergessen, dass ich das geschrieben habe !!!! Ich habe versucht, für mich selbst zu stimmen, aber leider kann ich nicht für mich selbst stimmen !!! ha ha
joe_evans
0

Meine Version des Einzeilers

#!/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:]*//
Valentin Heinitz
quelle
0

Ich habe gerade meinen eigenen Parser geschrieben. Ich habe versucht, verschiedene Parser zu verwenden, die hier zu finden sind. Keiner scheint sowohl mit ksh93 (AIX) als auch mit bash (Linux) zu funktionieren.

Es ist ein alter Programmierstil - Zeile für Zeile analysieren. Ziemlich schnell, da nur wenige externe Befehle verwendet wurden. Etwas langsamer, da alle Bewertungen für den dynamischen Namen des Arrays erforderlich sind.

Das INI unterstützt 3 spezielle Syntaxen:

  • includefile = ini-Datei -> Laden Sie eine zusätzliche ini-Datei. Nützlich, um ini in mehrere Dateien aufzuteilen oder eine Konfiguration erneut zu verwenden
  • includeir = Verzeichnis -> Wie includefile, jedoch mit einem vollständigen Verzeichnis
  • includeection = section -> Kopieren Sie einen vorhandenen Abschnitt in den aktuellen Abschnitt.

Ich habe alle diese Syntax verwendet, um eine ziemlich komplexe, wiederverwendbare INI-Datei zu haben. Nützlich, um Produkte bei der Installation eines neuen Betriebssystems zu installieren - das tun wir oft.

Auf Werte kann mit $ {ini [$ section. $ Item]} zugegriffen werden. Das Array MUSS vor dem Aufruf definiert werden.

Habe Spaß. Hoffe, es ist nützlich für jemand anderen!

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"
    }
user3637822
quelle
0

Diese Implementierung nutzt awkund hat die folgenden Vorteile:

  1. Gibt nur den ersten passenden Eintrag zurück
  2. Ignoriert Zeilen, die mit a beginnen ;
  3. Trimmt führende und nachfolgende Leerzeichen, jedoch keine internen Leerzeichen

Formatierte Version :

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

Einzeiler :

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

Wenn ich in base64 ein Passwort verwende, setze ich das Trennzeichen ":", da die base64-Zeichenfolge möglicherweise "=" hat. Zum Beispiel (ich benutze ksh):

> echo "Abc123" | base64
QWJjMTIzCg==

In parameters.inidie Zeile pass:QWJjMTIzCg==, und schließlich:

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

Wenn die Linie Leerzeichen wie " "pass : QWJjMTIzCg== "Hinzufügen" enthält | tr -d ' ', um sie zu trimmen:

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

Dies verwendet das System Perl und bereinigt reguläre Ausdrücke:

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

Die Antwort von "Karen Gabrielyan" unter anderen Antworten war die beste, aber in einigen Umgebungen, die wir nicht haben, wie bei einer typischen Busybox, habe ich die Antwort durch den folgenden Code geändert.

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
    }
Ehsan Ahmadi
quelle
Ich bin mir nicht ganz sicher, wie wahrscheinlich es ist, dass awk fehlt, aber sed, cut und eine relativ fortgeschrittenere bash-ähnliche Syntax sind verfügbar.
Ondrej K.
Die meisten anfänglichen Root-Dateisysteme implementieren / linuxrc oder / init als Shell-Skript und enthalten daher eine minimale Shell (normalerweise / bin / ash) sowie einige wichtige Dienstprogramme für den Benutzerbereich
Ehsan Ahmadi,
Sicher. Ich bin nur ein wenig überrascht, dass Sie Ihre Busybox ohne awk bauen würden, aber immer noch mit sed, cut und support für verschiedene "bashisms". Nicht, dass es nicht möglich wäre, ich wundere mich nur. ;)
Ondrej K.
Andere Werkzeuge sind leichter als awk. Wenn Sie ein Skript mit initramfs-tools in ubuntu distro in initramfs schreiben, werden Sie feststellen, dass Sie kein awk haben und auch andere Tools wie sed, grep ... sind in minimalem Betrieb.
Ehsan Ahmadi
Klar, ich spreche nicht von GNU awk oder einem anderen Full Blow Awk, sondern frage mich nur, wie viel man spart, wenn man die Busybox so konfiguriert, dass sie keine Awk-Unterstützung enthält (insbesondere, wenn die anderen genannten Bits nicht aus dieser Konfiguration entfernt werden). Könnte sein, dass * buntu initrd einfach so einen hat. Ich wundere mich nur über die Kombination / Auswahl, die alles ist.
Ondrej K.
0

Wenn Python verfügbar ist, werden im Folgenden alle Abschnitte, Schlüssel und Werte gelesen und in Variablen mit ihren Namen im Format "[Abschnitt] _ [Schlüssel]" gespeichert. Python kann INI-Dateien richtig lesen, daher nutzen wir sie.

#!/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
Hans Deragon
quelle
0

Sie können einen CSV-Parser xsv zum Parsen von INI-Daten verwenden.

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

oder aus einer Datei.

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

Wenn Sie Abschnitte verwenden, reicht dies aus:

Beispiel Rohausgabe:

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

Regexp-Analyse:

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

Jetzt alles zusammen:

$ 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
Yann Bizeul
quelle
0

Ich habe einen schönen Einzeiler (vorausgesetzt, Sie haben phpund jqinstalliert):

cat file.ini | php -r "echo json_encode(parse_ini_string(file_get_contents('php://stdin'), true, INI_SCANNER_RAW));" | jq '.section.key'
Midlan
quelle