Tags: PowerShell, Datei-Management
XML hat sich als Format für strukturierte Informationen etabliert, beispielsweise für Konfigurationsdateien oder den Datenaustausch. PowerShell kann XML-Dokumente als Objekte ansprechen und XML-Elemente als deren Eigenschaften. Hinzu kommen zahlreiche Methoden für den Zugriff auf Elemente und Attribute.
PowerShell verfolgt bei XML-Dokumenten grundsätzlich den gleichen Ansatz wie bei CSV-Dateien. Dort schirmt es den Benutzer von den Einzelheiten des Formats ab, so dass er sich nicht mit Überschriften, Spalten oder Trennzeichen herumschlagen muss. PowerShell übernimmt auch die grobe Arbeit beim Parsen von XML-Strukturen.
XML-Dokumente einlesen
Damit PowerShell eine XML-Datei als XML-Objekt darstellt, muss man sie an eine Variable vom Typ XML übergeben. Die Deklaration des Datentyps übernimmt dabei ein Cast. Das anschließende Einlesen erfolgt wie bei anderen Textdateien mit Get-Content:
[xml]$ovf = Get-Content -Path '.\MyVM.ovf'
Dieses Beispiel liest den Inhalt einer .ovf-Datei, die dem Austausch von virtuellen Maschinen zwischen verschiedenen Hypervisoren dient, in die Variable $ovf ein.
Ein alternatives Verfahren würde erst ein neues Objekt vom Typ XMLDocument erzeugen und anschließend dessen load-Methode verwenden, um die XML-Datei zu importieren:
$ovf = New-Object System.XML.XMLDocument
$ovf.Load(".\MyVM.xml")
Durch den DOM-Baum navigieren
Das XML-Objekt erlaubt nun eine unkomplizierte Navigation durch die Hierarchie mit Hilfe des Punkt-Operators. Nachfolger eines Elements lassen sich auf diese Weise einfach aneinanderreihen. PowerShell unterstützt diesen Vorgang durch die Autovervollständigung über die Tab-Taste.
In unserem Beispiel könnten wir ausgehend vom Wurzelelement mit
$ovf.Envelope.VirtualSystem.VirtualHardwareSection.Info
das Element Info unterhalb von VirtualHardwareSection ansprechen. Nachdem dieses nur einen Textknoten enthält, wird einfach nur dessen Inhalt ausgegeben. Benötigt man auch das Markup, dann würde man dieses über die Eigenschaft OuterXml erhalten:
$ovf.Envelope.VirtualSystem.VirtualHardwareSection.Info.OuterXml
Würden wir hingegen, wie im folgenden Fragment dargestellt, die Elemente Item ansteuern, dann bestünde die Ausgabe in einer Liste von Elementen bzw. Attributen und den Werten, die in jedem Item enthalten sind.
Der dafür zuständige Ausdruck sähe dann so aus:
$item = $ovf.Envelope.VirtualSystem.VirtualHardwareSection.Item
Will man auf das Element Description unterhalb des ersten Item zugreifen, dann könnte man das tun, indem man einen Index verwendet:
$ovf.Envelope.VirtualSystem.VirtualHardwareSection.Item[0].Description
Möchte man alle in einem Node enthaltenen Textknoten ausgeben, dann gelingt das mit der Eigenschaft InnerText, den Teilbaum unterhalb eines Elements zeigt man hingegen mit InnerXml an:
$ovf.Envelope.VirtualSystem.VirtualHardwareSection.Item.InnerText
Umgang mit Attributen
Über den Punkt-Operator lassen sich auch Attribute ansprechen, und zwar direkt oder über die Eigenschaft attributes. Allerdings wird man durch Eingabe von
$item.attributes
schnell erkennen, dass dabei nur der Name der Attribute ohne Präfix ausgegeben wird. Benötigt man ihre ganzen Namen, dann nutzt man dafür die gleichnamige Eigenschaft:
$item.attributes.name
Besitzen Attribute ein Namespace-Präfix, dann greift man auf die beiden Namensbestandteile mit Prefix und LocalName zu, also mit
$item.attributes.prefix
$item.attributes.localname
Für den expliziten Bezug auf den Wert eines Attributes gibt es noch die Eigenschaft Value.
Methoden für XML-Dokumente und Elemente
PowerShell stellt für Objekte des Typs System.Xml.XMLDocument zahlreiche Methoden bereit, der Aufruf von
$ovf | Get-Member -MemberType Method | measure -l
zählt 43 davon. Für System.Xml.XmlElement sind es 32, wobei es einige Überschneidungen mit jenen von XMLDocument gibt. Darunter finden sich mehrere, mit denen sich Knoten einfügen, klonen oder entfernen sowie Änderungen speichern lassen.
Hier wird man aber auch fündig, wenn man Informationen aus einem XML-Dokument entnehmen will. So kann man bestimmte Attribute mit GetAttribute() abfragen, indem man ihm den Namen des Attributs als Argument übergibt:
$item.GetAttribute("ovf:required")
Alternativ ließe sich die Ausgabe von Attributes mit Where-Object (Alias '?') filtern:
$item.attributes| ? Name -eq "ovf:required"
Ein derartiger Befehl ließe sich auch nutzen, um Attribute anhand ihres Wertes zu selektieren, wenn man statt Name hier Value einsetzt:
$item.attributes| ? Value -eq "false"| select name
Dieses Beispiel liefert alle Attribute der Item-Elemente, die in $item gespeichert sind und deren Wert gleich false ist.
Ob ein Element überhaupt Attribute hat, lässt sich mit HasAttribute nach dem Muster
$item.HasAttribute("InstanceID")
herausfinden.
Wahlfreier Zugriff auf Elemente
Elemente kann man nicht nur ansteuern, indem man dem Pfad vom Wurzelelement durch die gesamte Baumstruktur abwärts bis zum gewünschten Knoten folgt. PowerShell bietet in XMLDocument-Objekten mit GetElementsByTagName und GetElementById zwei Methoden, die man schon von Javascript kennt.
Wie der Name nahelegt, übergibt man der ersten der beiden Funktionen den Namen von Elementen und erhält als Ergebnis sämtliche Vorkommnisse unabhängig von ihrer Position in der Struktur:
$ovf.GetElementsByTagName("Info")
Dieser Befehl würde in unserem Beispieldokument alle Elemente mit dem Namen Info liefern.
Dagegen erhält man mit GetElementById() immer nur ein Element, weil der Wert eines id-Attributs in einem Dokument bekanntlich immer nur einmal vorkommen darf. Angenommen, unsere Datei enthielte das Fragment
<Info id="hardware">Virtual hardware requirements</Info>
Dann könnte man dieses Element mit
$ovf.GetElementById("hardware")
ansprechen.
Täglich Know-how für IT-Pros mit unserem Newsletter
Verwandte Beiträge
Weitere Links
1 Kommentar
Die folgende Methode zum Lesen einer XML-Datei ist fehlerhaft, da sie die Kodierung der XML-Datei kaputt machen kann:
[xml]$ovf = Get-Content -Path '.\MyVM.ovf'
Get-Content berücksichtigt nicht das "encoding" Attribut der XML-Datei, sondern nur ein evtl. vorhandenes BOM. Wenn kein BOM vorhanden ist (oftmals bei aus dem Netz heruntergeladenen Dateien), funktioniert die Methode rein zufällig, da die meisten XML-Dateien UTF-8 kodiert sind und Get-Content standardmäßig auf UTF-8 zurückfällt, wenn kein BOM vorhanden ist.
Die einzige wirklich korrekte Methode ist:
$ovf = New-Object System.XML.XMLDocument
$ovf.Load(".\MyVM.xml")
Hier wird das Encoding-Attribut aus dem Header der XML-Datei ausgelesen und die Datei entsprechend dekodiert.