Wie kann ich eine INI-Datei analysieren, deren Werte bestimmte Zeichen enthalten können?

Wie kann ich eine INI-Datei analysieren, deren Werte bestimmte Zeichen enthalten können?

Ich habe mir ein paar Bash-Ini-Parsing-Skripte angesehen und festgestelltDaseines wurde hier ein paar Mal verwendet, also versuche ich herauszufinden, ob es bei mir funktioniert. Es sieht so aus, als würde es die INI-Datei mehrere Male Zeile für Zeile lesen und mit jedem Durchgang schrittweise eine Funktion erstellen, die schließlich ausgewertet wird. Bei einigen Sonderzeichen funktioniert es einwandfrei, bei anderen jedoch nicht. Wenn ein Wert in der Datei ein einfaches Anführungszeichen oder ein Größer/Kleiner-als-Symbol enthält, gibt das Skript Syntaxfehler zurück. Auch andere Symbole führen zu unerwarteten Ergebnissen. Wie kann ich mit diesen Zeichen umgehen, wenn sie auftreten?

Dies ist die Funktion, die die INI analysiert.

#!/usr/bin/env bash
cfg_parser ()
{
    ini="$(<$1)"                # read the file
    ini="${ini//[/\[}"          # escape [
    ini="${ini//]/\]}"          # escape ]
    IFS=$'\n' && ini=( ${ini} ) # convert to line-array
    ini=( ${ini[*]//;*/} )      # remove comments with ;
    ini=( ${ini[*]/\    =/=} )  # remove tabs before =
    ini=( ${ini[*]/=\   /=} )   # remove tabs be =
    ini=( ${ini[*]/\ =\ /=} )   # remove anything with a space around =
    ini=( ${ini[*]/#\\[/\}$'\n'cfg.section.} ) # set section prefix
    ini=( ${ini[*]/%\\]/ \(} )    # convert text2function (1)
    ini=( ${ini[*]/=/=\( } )    # convert item to array
    ini=( ${ini[*]/%/ \)} )     # close array parenthesis
    ini=( ${ini[*]/%\\ \)/ \\} ) # the multiline trick
    ini=( ${ini[*]/%\( \)/\(\) \{} ) # convert text2function (2)
    ini=( ${ini[*]/%\} \)/\}} ) # remove extra parenthesis
    ini[0]="" # remove first element
    ini[${#ini[*]} + 1]='}'    # add the last brace
    eval "$(echo "${ini[*]}")" # eval the result
}

ini-Datei

[Section1]
value1=abc`def # unexpected EOF while looking for matching ``'
value2=ghi>jkl # syntax error near unexpected token `>'
value3=mno$pqr # executes ok but outputs "mnoqr"
value4=stu;vwx # executes ok but outputs "stu"

Antwort1

Die Tatsache, dass Siedürfenetwas tun bashbedeutet nicht, dass Siesollen.

sh(und bashetc.) Skripte eignen sich am besten als relativ einfache Wrapper zum Starten von Programmen oder um Textverarbeitungsbefehle. Für kompliziertere Aufgaben, einschließlich der Analyse von INI-Dateien und deren Bearbeitung, sind andere Sprachen besser geeignet. Haben Sie schon einmal darüber nachgedacht, Ihr Skript in perloder zu schreiben python? Beide haben gute .ini-Dateiparser – ich habe Config::INIdas Modul von Perl mehrmals verwendet, wenn ich eine INI-Datei analysieren musste.

Wenn Sie jedoch darauf bestehen, dies in Bash zu tun, sollten Sie ein assoziatives Array verwenden, anstatt einzelne Variablen festzulegen.

Beginnen Sie etwa so:

#! /bin/bash

inifile='user1074170.ini' 

# declare $config to be an associative array
declare -A config

while IFS='=' read -r key val ; do 
    config["$key"]="$val"
done <  <(sed -E -e '/^\[/d
                     s/#.*//
                     s/[[:blank:]]+$|^[[:blank:]]+//g' "$inifile" )

# now print out the config array
set | grep '^config='

Das sedSkript löscht die [Section1]Zeile (genauer gesagt alle Zeilen, die mit einer öffnenden eckigen Klammer beginnen [- in einer INI-Datei mit mehreren Abschnitten sollten Sie dies anders handhaben [1] ) und entfernt Kommentare sowie führende und nachfolgende Leerzeichen. Die whileSchleife liest jede Zeile ein, verwendet dabei =als Feldtrennzeichen und weist den Inhalt den Variablen $key und $val zu, die dann dem Array $config hinzugefügt werden.

Ausgabe:

config=([value1]="abc\`def" [value3]="mno\$pqr" [value2]="ghi>jkl" [value4]="stu;vwx" )

Sie können die Array-Einträge später in Ihrem Skript wie folgt verwenden:

$ echo value1 is "${config[value1]}"
value1 is abc`def

$ [ "${config[value4]}" = 'stu;vwx' ] && echo true
true

[1] awkoder perlhaben Sie bequeme und einfache Möglichkeiten, Dateien im "Absatz"-Modus zu lesen. Ein Absatz ist definiert als ein Textblock, der von anderen Textblöcken durch eine oder mehrere Leerzeilen getrennt ist.

Um beispielsweise nur mit zu arbeiten [Section1], fügen Sie das awkfolgende Skript unmittelbar vor dem sedSkript ein, das in die whileobige Schleife einspeist:

awk -v RS= -v ORS='\n\n' '/\[Section1\]/' "$inifile" | sed ...

(und natürlich "$inifile"vom Ende der sedBefehlszeile aus entfernen – Sie möchten die Datei nicht erneut eingeben, nachdem Sie sich die Mühe gemacht haben, nur [Section1]daraus zu extrahieren).

Die Einstellung ORSist nicht unbedingt erforderlich, wenn Sie nur einen Abschnitt aus der INI-Datei extrahieren. Sie ist jedoch nützlich, um die Absatztrennung beizubehalten, wenn Sie zwei oder mehr Abschnitte extrahieren.

Antwort2

Ich weiß, dass die Antwort unvollständig ist, aber MySQL.lnsin augeas scheint das meiste davon analysieren zu können. In augtool:

augtool> set /augeas/load/testini/incl "/root/test.ini"
augtool> set /augeas/load/testini/lens "MySQL.lns"
augtool> load
augtool> ls /files/root/
.ssh/      test.ini/
augtool> ls /files/root/test.ini
target/ = Section1
augtool> ls /files/root/test.ini/target/
value1/ = abc`def
value2/ = ghi>jkl
value3/ = mno$pqr
value4/ = stu

Das einzige, bei dem es schiefgelaufen ist, ist das letzte, und um ehrlich zu sein, glaube ich nicht, dass das ein Fehler ist. In .iniDateien markiert das Semikolon den Anfang eines Kommentars. Ich möchte auch fragen, ob Ihre Daten tatsächlich so aussehen.

Wenn dies der Fall ist, können Sie sedvorher einfach etwas tun, das ;auf einen unbenutzten Zeichenwert gesetzt wird, und es dann nachträglich zurücktransformieren. Letztendlich werden Sie jedoch einige Standards benötigen, damit die Datei eine erkennbare Struktur haben kann.

BEARBEITEN:

Ich habe es mit dem PHP-Objektiv ausprobiert und solange die Werte in Anführungszeichen standen, funktionierte alles:

[root@vlzoreman ~]# augtool
augtool> set /augeas/load/testini/lens "PHP.lns"
augtool> set /augeas/load/testini/incl "/root/test.ini"
augtool> load
augtool>  ls /files/root/test.ini/Section1/
value1 = abc`def
value2 = ghi>jkl
value3 = mno$pqr
value4 = stu;vwx

Ansonsten ist es so weit gekommen wie die MySQL-Linse.

BEARBEITEN #2:

Ich bin sicher, dass es eine einfachere Möglichkeit gibt, dies zu schreiben, aber dies ist ein Verwendungsbeispiel:

[root@vlp-foreman ~]# bash bash.sh
Values for: Section1:
        :: value1 is abc`def
        :: value2 is ghi>jkl
        :: value3 is mno$pqr
        :: value4 is stu;vwx
Values for: Section2:
        :: value1 is abc`def

Das Skript lautet:

#!/bin/bash

sections=$(augtool -A --transform "PHP.lns incl /root/test.ini" ls /files/root/test.ini | cut -f1 -d/)

for currentSection in $sections; do

  echo "Values for: $currentSection:"

  fields=$(augtool -A --transform "PHP.lns incl /root/test.ini" ls /files/root/test.ini/$currentSection | awk '{print $1}')

  for currentField in $fields; do

    currentValue=$(augtool -A --transform "PHP.lns incl /root/test.ini" print /files/root/test.ini/$currentSection/$currentField | cut -f2 -d=)
    currentValue=$(echo $currentValue | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//' | sed -e 's/^"//' -e 's/"$//')

    echo -e "\t:: $currentField is $currentValue"

  done

done

Antwort3

schau dir mal an crudini:https://www.pixelbeat.org/programs/crudini/
Unter Ubuntu können Sie es installieren, um sudo apt install crudini
einen Wert aus einer INI-Datei zu lesen. Führen Sie dazu Folgendes aus:

$ value1=$(crudini --get "crudini.ini" "Section1" "value1")

Crudini unterstützt mehrere INI-Dateiformate und verarbeitet Sonderzeichen:

crudini.ini

[Section1]
value1=abc`def
value2=ghi>jkl
value3=mno$pqr
value4=stu;vwx

Werte ablesen

$ for i in {1..4}; do crudini --get "crudini.ini" "Section1" "value$i"; done
abc`def
ghi>jkl
mno$pqr
stu;vwx
$

verwandte Informationen