PowerShell und INI-Dateien
Aus Windows 16bit-Zeiten können die älteren Leser sicher noch die INI-Dateien (Win.ini, System.ini) und selbst heute gibt es immer noch Programme, die auf diese Weise Einstellungen oder sogar Daten abspeichern. Es gibt leider keine native Unterstützung von INI-Dateien in PowerShell aber da es sich um Textdateien handelt, können diese durchaus per PowerShell gelesen, ausgewertet und geschrieben werden. Ich möchte das Rad hier nicht neu erfinden und einer alten Technik Vorschub leisten aber zumindest "lesen" muss ich solche Dateien doch, wie z.B. das Projekt KlimaLogg Pro wieder gezeigt hat.
Aufbau von INI-Dateien
eine INI-Datei hat schon von immer den gleichen Aufbau und sie können selbst auf einem Windows 7 Desktop immer noch die Datei "C:\Windows\win.ini" finden und öffnen. Abschnitte werden mit "eckigen Klammern" gestartet, und darauf folgen Key und Werte, die durch ein Gleichheitszeichen getrennt sind.
; Kommentar eine Beispiel INI-Datei [Bereich1] Key1=Wert1 Key2=Wert2 [Bereich2] Key1=Wert1 Key2=Wert2
Während Bereiche nur einmal vorkommen sollten, können Key können durchaus mehrfach genutzt werden. Über die Interpretation der Werte muss die Anwendung bestimmen. Kommentare sind durch ein Semikolon einzuleiten.
- INI file
http://en.wikipedia.org/wiki/INI_file - Initialisierungsdatei
http://de.wikipedia.org/wiki/Initialisierungsdatei - INI file syntax
http://code.google.com/p/minini/wiki/INI_File_Syntax
Windows Bordmittel
Wenn Windows selbst INI-Dateien nutze, dann kann man davon ausgehen, dass es auch entsprechende API-Schnittstellen gibt. Schließlich sind Programmierer auch bequem und das Rad muss ja nicht immer neu erfunden werden. Die entsprechenden Funktionen sind in der MSDN gut beschrieben
- GetPrivateProfileString
http://msdn.microsoft.com/de-de/library/ms724353.aspx
Microsoft Windows API-Funktion zum Auslesen von Werten aus einer INI-Datei. - GetPrivateProfileSection
http://msdn.microsoft.com/de-de/library/ms724348(VS.85).aspx
Microsoft Windows API-Funktion zum Auslesen von allen Schlüsseln und Werten aus einer Sektion der INI-Datei. - GetPrivateProfileSectionNames
http://msdn.microsoft.com/de-de/library/ms724352(VS.85).aspx
Microsoft Windows API-Funktion zum Auslesen von Sektionen aus einer INI-Datei.
Allerdings sind dies "Win32 API"-Funktionen und kein Managed Code für die einfache direkte Nutzung in C# oder PowerShell. Es gibt allenfalls andere Projekte, die eine INI-Verarbeitung nachprogrammiert haben
- An INI file handling class using C#
http://www.codeproject.com/csharp/cs_ini.asp - Convert INI file to XML
http://www.codeproject.com/Articles/2033/Convert-INI-file-to-XML
http://www.codeproject.com/KB/cs/ini2xml.aspx
Andere gehen den Weg und konvertieren eine INI-Datei in einem XML-Struktur, auf die sie dann wie gewohnt zugreifen können.
Mein PowerShell-Ansatz zum Lesen
Nein, ich möchte das Rad nicht neu erfinden und daher auch keine neue Klasse für INI-Dateien erstellen. Ich habe aber bei einem anderen Skript (Siehe KlimaLogg Pro - Servermonitoring) den Bedarf eine INI-Datei auszulesen um die Daten in PRTG einzulesen und das sollte schnell und möglichst ohne Abhängigkeiten gehen. In dem Fall war auch das Formt der INI-Datei bekannt und wurde nicht durch andere Programme verändert. Ich konnte einen ganz einfach Ansatz wählen, mit dem INI-Dateien eingelesen werden können
param ( $inputfile ) $inifile = get-content $inputfile $resulttable=@() foreach ($line in $inifile) { write-host "Processing $line" if ($line[0] -eq ";") { write-host "Skip comment line" } elseif ($line[0] -eq "[") { $segment = $line.replace("[","").replace("]","") write-host "Found new segment: $segment" } elseif ($line -like "*=*") { write-host "Found Keyline" $resulttable += New-Object PSObject -Property @{ segment = $segment Key = $line.split("=")[0] value = $line.split("=")[1] } } else { write-host "Skip line" } } $resulttable
So erhalte ich eine Tabelle aus den Spalten "Segment", "Key" und "Value".
Key value segment -------- ----- ------- programm_name KlimaLogg Pro header programm_version 1.0 header file_format_version 1.0 header last_actualisation "3605771885" time deg_C "22.9" temperature_channel_0 deg_F "73.3" temperature_channel_0
Und darauf lässt sich vortrefflich mit WHERE filtern.
where {$_.Key -eq "deg_C" -and $_.segment -like "temp*"}
Dieses einfache Script ist natürlich nicht in der Lage, eine INI-Datei auch zu ändern. Es ist "ReadOnly".
INI und XML
Zudem ist der Zugriff über eine Liste elegant aber ein direkter Zugriff geht damit doch nicht so einfach. Daher habe ich mir gedacht, das man eine INI-Datei doch ganz einfach in eine XML-Struktur überführen kann. Und hier das Beispiel:
; Kommentar eine Beispiel INI-Datei <inifile> [Bereich1] <Bereich1> Key1=Wert1 <Key1>Wert1</Key1> Key2=Wert2 <Key2>Wert2</Key2> </Bereich1> [Bereich2] <Bereich2> Key1=Wert1 <Key1>Wert1</Key1> Key2=Wert2 <Key2>Wert2</Key2> </Bereich1> </inifile>
So übertragen kann man sehr direkt auf einen Wert zugreifen
$wert = $inixml.bereitname.key
Gesagt, getan und mit wenigen PowerShell-Zeilen dürften die meisten INI-Dateien zu einer XML-Struktur in einer Variable zur weiteren Verwendung werden
$inifile = get-content $inputfile [string]$resultxml="<inifile>" [string]$inisection="" foreach ($line in $inifile) { write-host "Processing $line" if ($line[0] -eq "[") { if ($inisection -ne "") { write-host "Closing old inisection: $inisection" $resultxml+=("</$inisection>`r`n") } $inisection = $line.replace("[","").replace("]","") write-host "Found new inisection: $inisection" $resultxml+=("<$inisection>`r`n") } elseif ($line -like "*=*") { $key=($line.split("=")[0]) $value=($line.split("=")[1]) write-host "Found parameterline Key: $key Value:$value" $resultxml+=("<$key>$value</$key>`r`n") } else { write-host "Skip line" } } $resultxml+=("</$inisection>`r`n") $resultxml+="</inifile>" $resultxml
Hier ist natürlich keinerlei Validierung oder Fehlerbehandlung vorhanden.
Und was ist mit Schreiben ?
Ich habe am Anfang gesagt, dass ich nur "Lesen" möchte und das hat auch Gründe. Diese einfachen Routinen lesen die INI-Datei ein, um die Daten in Programmen zu verwenden. Aber Sie lesen nicht alle Informationen ein, So werden Formatierungen, Leerzeilen und Kommentare einfach übersprungen. Würde ich diese Daten einfach schreiben, bestände die Gefahr dass Informationen verloren gehen. Insbesondere, wenn schon beim Lesen etwas vergessen oder falsch interpretiert wurde.
Die Code-Beispiele sind alle ohne Fehlerbehandlung und Berücksichtigung von Sonderfällen und es ist einfach zu gefährlich mit diesem "Einfachcode" auch noch zu schreiben. Zudem sind INI-Dateien sowieso "out" und XML-Daten oder die Registrierung hierfür viel besser geeignet. Mir reicht es die Daten von INI-Dateien zu lesen. Auslöser für diese Seite war ja das Parsen einer INI-Datei von KlimaLogg Pro Aber wen Sie die geniale Routine für INI-Dateien geschrieben haben, dann senden Sie mir einfach einen Link, auf den ich verweisen kann.
Weitere Links
- PoS Code
Get-PrivateProfileString
http://poshcode.org/2161 - Managing INI files with
PowerShell
http://www.leeholmes.com/blog/2007/10/02/managing-ini-files-with-PowerShell/ - Use PowerShell to Work with
Any INI File
http://blogs.technet.com/b/heyscriptingguy/archive/2011/08/20/use-PowerShell-to-work-with-any-ini-file.aspx - import-Ini.ps1 imports ini file
http://gallery.technet.microsoft.com/scriptcenter/Imports-ini-file-7143b6aa - How to read Keyeters from an ini file
http://www.zerrouki.com/how-to-read-Keyeters-from-an-ini-file/ - Working with INI style files in PowerShell
http://sqlchow.wordpress.com/2013/01/20/working-with-ini-style-files-in-PowerShell/ - Use PowerShell to Work with Any INI File
http://blogs.technet.com/b/heyscriptingguy/archive/2011/08/20/use-PowerShell-to-work-with-any-ini-file.aspx - Edit old fashioned INI files
with PowerShell
http://gallery.technet.microsoft.com/scriptcenter/Edit-old-fashioned-INI-f8fbc067 - Why are INI files deprecated
in favor of the registry?
http://blogs.msdn.com/b/oldnewthing/archive/2007/11/26/6523907.aspx