Tags: PowerShell, Datei-Management
Um Informationen aus XML-Dokumenten zu extrahieren gibt es mit XPath eine vom W3C standardisierte Abfragesprache. Damit kann man wesentlich komplexere Operationen ausführen als mit der Punkt-Notation von PowerShell. Umständlich wird XPath im Zusammenspiel mit Namespaces.
XML Path Language (XPath) erlaubt etwa, Elemente abhängig von Attributwerten zu selektieren, Geschwisterknoten über Positionsparameter anzusprechen oder die Navigation über eine verkürzte Syntax sowie Wildcards (siehe dazu die XPath-Beispiele bei W3C).
XPath in Methoden von XMLDocument
PowerShell sieht die Verwendung von XPath-Ausdrücken ebenfalls vor, und zwar über die Methoden SelectNodes() und SelectSingleNode(). Sie stehen in Objekten des Typs System.Xml.XMLDocument und System.Xml.XMLElement zur Verfügung. Die erste der beiden liefert alle Knoten, auf welche die Abfrage zutrifft, die zweite nur das erste Vorkommen.
Für die folgenden Ausführungen bedienen wir uns eines Fragments aus einer .ovf-Datei, die dem Austausch von VMs über Plattformen hinweg dient:
Nachdem man die Datei mit
[xml]$ovf = Get-Content -Path '.\MyVM.ovf'
eingelesen hat, könnte man alle Item-Elemente so adressieren:
$ovf.DocumentElement.SelectNodes("//Item")
In der Praxis enthalten heute fast alle XML-Dokumente zumindest eine Namespace-Deklaration, das hier als Beispiel verwendete OVF-Format gleich mehrere:
In diesem Fall würde jeder Aufruf von SelectSingleNode() oder SelectNodes() nach obigem Muster kommentarlos zu einem leeren Ergebnis führen.
Abhilfe schafft hier das Erzeugen eines XmlNamespaceManager-Objekts, das man im Aufruf der zwei Funktionen übergeben muss:
$ns = New-Object System.Xml.XmlNamespaceManager($ovf.NameTable)
$ns.AddNamespace("ns","http://schemas.dmtf.org/ovf/envelope/1")
Der erste Befehl erstellt das Objekt und der zweite fügt den Default Namespace hinzu. Interessant dabei ist, dass Letzterer ja kein Präfix definiert, aber wir für den XmlNamespaceManager ein solches benötigen. Dieses Beispiel verwendet "ns", das wir dann brauchen, um uns auf Elemente oder Attribute zu beziehen, die kein Präfix haben:
$ovf.SelectNodes("//ns:Item",$ns)
Wie man hier sieht, nutzt der XPath-Ausdruck nun das Präfix für den Default Namespace und als zweites Argument übergeben wir den oben definierten XmlNamespaceManager. Für eine vollständige Verfügbarkeit aller Elemente müsste man natürlich alle Namespaces hinzufügen.
Wenn man sich die dafür nötige Tipparbeit sparen möchte, dann kann man dieses Script von Github herunterladen. Es liest alle Namespace-Definitionen aus und erzeugt daraus einen neuen XmlNamespaceManager.
XPath-Abfragen mit Select-Xml
Für XPath-Abfragen bietet PowerShell mit Select-Xml ein eigenes Cmdlet, welches einen vereinfachten Zugriff auf XML-Dateien erlaubt. Diese müssen dabei nicht explizit in ein XML-Objekt eingelesen werden, vielmehr übergibt man ihm den Pfad zur Datei einfach über den Parameter Path:
Select-Xml -Path '.\MyVM.ovf' -XPath "//Item"
Nachdem die OVF-Datei mehrere Namespace-Deklarationen enthält, liefert auch dieser Aufruf ganz lapidar kein Ergebnis. Die Lösung besteht hier jedoch nicht in der Definition eines XmlNamespaceManager, sondern man erstellt eine Hash-Tabelle nach dem Muster
$namespace = @{Präfix=URI; Präfix=URI; …}
und übergibt diese dem Parameter Namespace:
Select-Xml -Path '.\MyVM.ovf' -XPath "//ns:Item" -Namespace $namespace
Wie man hier sieht, bezieht sich das Präfix "ns" wieder auf den Default Namespace. Man kann dafür im Hash-Table auch ein beliebiges anderes Präfix wählen, das man aber dann natürlich auch in der XPath-Abfrage verwenden muss.
Eine Besonderheit von Select-Xml besteht darin, dass es ein Array zurückgibt, das aus Nodes, dem Pfad und dem Xpath-Ausdruck besteht. Um den Inhalt der Knoten aufzuschließen, setzt man Select-Object ein:
Select-Xml -Path '.\MyVM' -XPath "//ns:Item" -Namespace $namespace |
select -ExpandProperty Node
Alternativ greift man über den Punkt-Operator auf die Node-Eigenschaft zu:
(Select-Xml -Path '.\MyVM.ovf -XPath "//ns:Item" -Namespace $namespace).Node
Der Vorteil dieses Vorgehens besteht darin, dass man so ein Objekt vom Typ XmlElement erhält und all dessen Eigenschaften und Methoden nutzen kann. Dazu zählt etwa auch die Ausgabe der bloßen Textknoten mit InnerText oder des enthaltenen Teilbaums mit InnerXML:
(Select-Xml -Path '.\MyVM.ovf -XPath "//ns:Item" -Namespace $namespace).Node. InnerText
Täglich Know-how für IT-Pros mit unserem Newsletter
Ähnliche Beiträge
Weitere Links