Shellscript INI-Parser

Manchmal müssen auch Shell-Scripte konfigurierbar sein. Um mehrere, zufällige Playlisten für meine Musiksammlung zu erstellen, wollte ich ein Script haben, welches eine Konfigurationsdatei auswertet und für jeden Abschnitt die gleiche Aktion ausführt. Gleichzeitig sollte es sich durch falsche INI-Direktiven nicht durcheinander bringen lassen und wie eine gängige INI-Datei aufgebaut sein. Die Lösung ist folgendes Shellscript, welches eine INI-Datei abarbeitet und für jeden durch [Header] eingeleiteten Abschnitt eine Callbackfunktion ausführt:

function ParseINI() {
  inFile="\$1"
  callback="\$2"
  shift 2
  params="\$*"

  if [ ! -f "\$inFile" ]; then echo "File \$inFile not found!"; exit; fi

  local IFS="="

  # Am Ende eine leere Section anhängen, damit auch der letzte Abschnitt verarbeitet wird
  echo "[]"|cat "\$inFile" -|sed 's/\\t/ /g;s/^ +//;s/ +\$//;/^#/d;/^\$/d'|while read name value; do
    [ -z "\$name" ] && continue

    local IFS=" "
    if [ "\${name:0:1}" == "[" ]; then
      counter=0
      for param in \$params; do
        [ -z "\${sectionParams[\$counter]}" ] && sectionParams[\$counter]="''"
        let counter++
      done
      [ -z "\$section" ] || eval \$callback \\"\$section\\" \${sectionParams[*]}
      section=\${name/'['/}
      section=\${section/']'/}
      unset sectionParams
    else
      counter=0
      for param in \$params; do
        if [ "\$param" == "\${name/ /}" ]; then
          value=\${value/# /}
          value=\${value/% /}
          value=\${value/#\\"/}
          value=\${value/%\\"/}
          sectionParams[\$counter]="'\$value'"
        fi
        let counter++
      done
    fi
    local IFS="="
  done
}

Diese Funktion parsed eine INI-Datei mit folgendem Aufbau:

# Kommentar
[SectionHeader]
# Eventuell noch ein Kommentar
Name=Wert

Einrückungen werden ebenso entfernt wie am Schluss von Parametern hängende Leerzeichen. Auch Kommentarzeilen werden automatisch ignoriert.

Um die Funktion in einem Shellscript zu verwenden, müssen die Parameter folgendermaßen belegt werden:

Parameter Wert
1 Name der zu verarbeitenden INI-Datei.
2 Name der Callbackfunktion, welche für jeden INI-Abschnitt aufgerufen werden soll.
3-x Die Namen der INI-Werte in der Reihenfolge, in welcher sie an die Callbackfunktion übergeben werden sollen.

Die Reihenfolge der Parameter im INI-Abschnitt ist nicht relevant. Das Script übergibt die Parameter immer in der Reihenfolge, welche im Aufruf von ParseINI angegeben wurde. Wird ein Parameter im entsprechenden Abschnitt nicht gesetzt, so wird ein leerer Wert übergeben. Das folgende Beispiel würde also die Datei /etc/genplaylist.conf abarbeiten und für jeden Abschnitt die Bash-Funktion BuildPlaylist aufrufen. Die Parameter werden dabei in folgender Reihenfolge übergeben:

Parameter Wert
1 Name des INI-Abschnitts.
2 INI-Parameter BasePath.
3 INI-Parameter RelPath.
4 INI-Parameter OutFile.
ParseINI "/etc/genplaylist.conf" "BuildPlaylist" "BasePath" "RelPath" "OutFile"

Wichtig: Der Callback wird in einer Subshell ausgeführt. Es ist also nicht möglich, Umgebungsvariablen für die Hauptshell zu setzen!

Beispieldownload

Dieser Download enthält das Beispielscript und eine passende, kommentierte INI-Datei: herunterladen