2016-09-09 4 views
0

Ich bin ein paar Wochen in Bash-Scripting und ich habe noch nicht weit genug fortgeschritten, um meinen Kopf um dieses Problem zu wickeln. Jede Hilfe wäre willkommen!Wie können Schlüsselwertpaare aus einer Datei extrahiert werden, wenn sich Werte über mehrere Zeilen erstrecken?

Ich habe einen „script.conf“ Datei, die Folgendes enthält:

key1=value1 
key2=${HOME}/Folder 
key3=("k3v1" "k3 v2" "k3v3") 
key4=("k4v1" 
    "k4 v2" 
    "k4v3" 
) 
key5=value5 
#key6="Do Not Include Me" 

In einem Bash-Skript, ich den Inhalt dieser script.conf Datei in ein Array lesen möchten. Ich habe gelernt, wie man mit den Szenarien für die Schlüssel 1, 2, 3 und 5 umgeht, aber das key4-Szenario wirft einen Schraubenschlüssel hinein, der sich über mehrere Zeilen erstreckt.

Ich habe die Verwendung von sed -n '/=\s*[(]/,/[)]/{/' erforschen die Erfassung key4 und seinen Wert hat, aber ich kann nicht herausfinden, wie dies zu mischen, so dass die anderen Tasten auch in den Spielen erfaßt werden. Die Bereichssyntax ist auch neu für mich, also habe ich nicht herausgefunden, wie man den Schlüssel/Wert trennt. Ich habe das Gefühl, dass es einen einfachen Regex gibt, der das erreicht, was ich will ... im Klartext: "Finde und gruppiere das Muster ^(.*)= (für den Schlüssel), dann gruppiere alles nach dem '=' char, bis ein weiterer ^(.*)= Treffer gefunden wird spülen und wiederholen ". Ich denke, wenn ich das tue, muss ich die While-Read-Zeile ändern, um die Schlüssel/Wert-Trennung für mich nicht zu handhaben (ich werde das untersuchen, während ich auf eine Antwort warte). BTW, ich denke, eine Lösung, wo der Wert von key4 abgeflacht ist (neue Zeilen entfernt) wäre akzeptabel; Ich weiß, dass ich für key3 den Wert als String speichern und später in ein Array konvertieren muss, wenn ich darüber iterieren möchte, da ein Array-Element anscheinend keine Liste enthalten kann.

Bin ich auf dem richtigen Weg mit sed oder ist das ein Job für awk oder ein anderes Werkzeug? (Ich habe mich noch nicht in awk gewagt). Gibt es einen einfacheren Ansatz, den ich vermisse, weil ich zu tief in den Wald hinein bin (wie die Zeile while read in der LoadConfigFile-Funktion zu ändern)?

Hier ist der Code, den ich in bisher haben script.sh für die Verarbeitung und die anderen Paare in die $ config Array erfassen:

__AppDir=$(dirname $0) 
__AppName=${__ScriptName%.*} 


typeset -A config #init config array 
config=( #Setting Default Config values 
    [key1]="defaultValue1" 
    [key2]="${HOME}/defaultFolder" 

    [QuietMode]=0 
    [Verbose]=0  #Ex. Usage: [[ "${config[Verbose]}" -gt 0 ]] && echo ">>>Debug print" 
) 

function LoadConfigFile() { 
    local cfgFile="${1}" 
    shopt -s extglob #Needed to remove trailing spaces 
    if [ -f ${cfgFile} ]; then 
     while IFS='=' read -r key value; do 
      if [[ "${key:0:1}" == "#" ]]; then 
       #echo "Skipping Comment line: ${key}" 
      elif [ "${key:-EMPTY}" != "EMPTY" ]; then 
       value="${value%%\#*}" # Delete in-line, right comments 
       value="${value%%*()}" # Delete trailing spaces 
       value="${value%%()*}" # Delete leading spaces 
       #value="${value%\"*}" # Delete opening string quotes 
       #value="${value#\"*}" # Delete closing string quotes 

       #Manipulate any variables included in the value so that they can be expanded correctly 
       # - value must be stored in the format: "${var1}". `backticks`, "$var2", and "doubleQuotes" are left as is 
       value="${value//\"/\\\"}" # Escape double quotes for eval 
       value="${value//\`/\\\`}" # Escape backticks for eval 
       value="${value//\$/\\\$}" # Escape ALL '$' for eval 
       value="${value//\\\${/\${}" # Undo the protection of '$' if it was followed by a '{'   
       value=$(eval "printf '%s\n' \"${value}\"") 

       config[${key}]=${value} #Store the value into the config array at the specified key     
       echo " >>>DBG: Key = ${key}, Value = ${value}" 
      #else 
      # echo "Skipped Empty Key" 
      fi 

     done < "${cfgFile}" 
    fi 
} 

CONFIG_FILE=${__AppDir}/${__AppName}.conf 
echo "Config File @ ${CONFIG_FILE}" 

LoadConfigFile ${CONFIG_FILE} 

#Print elements of $config 
echo "Script Config Values:" 
echo "----------------------------" 
for key in "${!config[@]}"; do  #The '!' char gets an array of the keys, without it, we would get an array of the values 
    printf " %-20s = %s\n" "${key}" "${config[${key}]}" 
done 
echo "------ End Script Config ------" 

#To convert to an array... 
declare -a valAsArray=${config[RequiredAppPackages]} #Convert the value from a string to an array 
echo "Count = ${#valAsArray[@]}" 
for itemCfg in "${valAsArray[@]}"; do 
    echo " item = ${itemCfg}" 
done 

Wie ich schon erwähnt, ich bin nur zum Start lerne Bash und Linux Scripting allgemein, also wenn du siehst, dass ich auch in anderen Bereichen meines Codes einige Tabus mache, gib bitte Feedback in den Kommentaren ... Ich möchte keine schlechten Gewohnheiten früh anfangen auf :-).

* Wenn es darauf ankommt, ist das Betriebssystem Ubuntu 14.04.

EDIT: Wie gewünscht, nachdem die script.conf Datei zu lesen, würde ich für die Elemente in $config[@] gerne folgenden gleichwertig sein:

typeset -A config #init config array 
config=( 
    [key1]="value1" 
    [key2]="${HOME}/Folder" 
    [key3]="(\"k3v1\" \"k3 v2\" \"k3v3\")" 
    [key4]="(\"k4v1\" \"k4 v2\" \"k4v3\")" 
    [key5]="value5" 
) 

Ich mag die konvertieren können, Werte der Elemente ‚key4‘ und ‚key3‘ in ein Array und über sie die gleiche Art und Weise in den folgenden Code iteriert:

declare -a keyValAsArray=${config[keyN]} #Convert the value from a string to an array 
echo "Count = ${#keyValAsArray[@]}" 
for item in "${keyValAsArray[@]}"; do 
    echo " item = ${item}" 
done 

ich glaube nicht, dass es wichtig ist, wenn \ n für key4 die va erhalten bleibt lue oder nicht ... das hängt davon ab, ob declare ein Problem damit hat.

+1

Ist das Sourcing der Konfigurationsdatei keine Option? –

+1

Anstatt eine fehlerhafte Teilmenge eines Bash-Parsers zu implementieren, warum sollte man die Datei nicht _sourcen_? –

+0

Sind Sie mit der Shell eingeschränkt? Für solche Aufgaben würde ich eine andere * Sprache * (Python, Perl, ...) verwenden. – MarcoS

Antwort

0

Eine Shell ist eine Umgebung, in der Tools mit einer Sprache aufgerufen werden können, um diese Aufrufe zu sequenzieren. Es ist kein Werkzeug, um Text zu manipulieren. Das Standard-UNIX-Tool zum Bearbeiten von Text ist awk.Versuchen, Text in der Shell zu manipulieren, ist eine schlechte Angewohnheit, siehe why-is-using-a-shell-loop-to-process-text-considered-bad-pr‌​actice für einige der Gründe, warum

Sie noch nicht das erwartete Ergebnis der Bestückung der Config-Array, so dass ich nicht sicher bin, aber ich denke, das ist, was Sie wollte:

$ cat tst.sh 
declare -A config="($(awk ' 
    { gsub(/^[[:space:]]+|([[:space:]]+|#.*)$/,"") } 
    !NF { next } 
    /^[^="]+=/ { 
     name = gensub(/=.*/,"",1) 
     value = gensub(/^[^=]+=/,"",1) 
     n2v[name] = value 
     next 
    } 
    { n2v[name] = n2v[name] OFS $0 } 
    END { 
     for (name in n2v) { 
      value = gensub(/"/,"\\\\&","g",n2v[name]) 
      printf "[%s]=\"%s\"\n", name, value 
     } 
    } 
    ' script.conf 
))" 

declare -p config 

$ ./tst.sh 
declare -A config='([key5]="value5" [key4]="(\"k4v1\" \"k4 v2\" \"k4v3\")" [key3]="(\"k3v1\" \"k3 v2\" \"k3v3\")" [key2]="/home/Ed/Folder" [key1]="value1")' 

Die für gensub() GNU awk oben verwendet, mit anderen awks Sie [g] sub() stattdessen verwenden würde.

+1

Danke für die Antwort. Ich hatte noch keine Chance, deinen Code zu testen ... hoffentlich heute Nachmittag. Wenn alles so läuft, wie ich es brauche, bin ich zurück, um dies als die richtige Antwort zu markieren. Wenn nicht, werde ich versuchen, ein Beispiel zu geben, was ich brauche. –

+0

@JerrenSaunders hat das für dich funktioniert oder nicht? –

+0

Schließen, aber nicht ganz was ich brauche. Ich arbeite an einer weiteren Bearbeitung, die hoffentlich klarer wird. –

Verwandte Themen