Flexible Typenkonvertierung dank PSTypeConverter – ein kleines Beispiel erklärt wie die Typenkonvertierung bei der PowerShell funktioniert

Die in diesem Blog-Eintrag vorgestellte Technik ist zwar unabhängig von einer bestimmten PowerShell-Version – für die Umsetzung des Beispiels wird die Version 3.0 vorausgesetzt, wenngleich es mit der Version 2.0 theoretisch auch funktionieren sollte

Eine der Stärken der PowerShell ist die flexible Typenkonvertierung und -erweitererung. Dazu gehört vor allem die Fähigkeit, durch Typenkonvertierung an Objekte weitere Members anzuhängen. Dies geschieht immer so, dass es der Anwender nicht mitbekommt, sofern er oder sie überhaupt mit dem etwas anspruchsvolleren Konzept der Typen vertraut ist und/oder sich überhaupt dafür interessiert.

Wer z.B. ein Get-Process-Cmdlet ausführt, erhält zwar Objekte vom Typ System.Diagnostics.Process zurück, doch in „Wirklichkeit“ basieren die Objekte auf einem dynamischen Typ, der aus dem PowerShell-Typensystem hervorgegangen ist und um zahlreiche Members erweitert wurde. Das „wahre“ Objekt stellt jedes PowerShell-Objekt über seine PsObject-Eigenschaft zur Verfügung. Die Grundlage für die Typenerweiterung beim Process-Objekt ist die Datei Types.ps1xml im PowerShell-Installationsverzeichnis, in der die nachträglich angehängten Members für den Typ System.Diagnostics.Process definiert werden. Ein Beispiel: Die Eigenschaft Ws wird als AliasProperty nachträglich angehängt, damit die Eigenschaft mit dem etwas sperrigen Namen Workingset64 (bzw. Workingset) über einen kurzen Namen abgesprochen werden kann.

Die Typenkonvertierung eines Objekts kann auch dynamisch erfolgen, also ohne, dass die zusätzlich hinzugefügten Members bereits festgelegt wurden. Ein Beispiel ist der Typ-Alias [Xml], der Text in ein System.Xml.XmlDocument-Objekt konvertiert, sofern es sich um wohlgeformtes XML handelt, und dabei die Elemente und Attribute als Eigenschaften anhängt.

Durch den Xml Typalias findet eine Typenkonvertierung statt. Aus Text vom Typ String wird ein XmlDocument-Objekt, das auf dem gleichnamigen Typ basiert. Die Grundlage für die Typenkonvertierung ist eine Klasse, die sich von der PSTypeConverter-Klasse im System.Management.Automation ableitet. Benötigt man einen eigenen Typenkonvertierer, muss man „nur“ eine .Net-Assembly erstellen, in der diese Klasse abgeleitet und die Typenkovnertierung in der überschriebenen ConvertFrom-Methode durchgeführt wird.

Beispiele für den praktischen Einsatz von PsTypeConverter sind etwas schwer zu finden. Ein sehr guter Blog-Eintrag von Glenn Sizemore macht das Prinzip der Umsetzung deutlich und hat mich dazu motiviert, das Thema ebenfalls endlich einmal aufzugreifen (eigentlich steht das seit jenem PowerShell Deep Dive aus, der 2010 in Frankfurt a.M. stattfand):

http://practical-admin.com/blog/powershell-custom-types-type-conversion-and-ets/

Dynamische Typenkonvertierung an einem Beispiel

Das folgende Beispiel zeigt die Umsetzung eines Typenkonverters, der aus einer Ini-Datei ein Objekt macht, über das die Sektionen der Datei und die Einträge innerhalb einer Sektion zur Verfügung gestellt werden. Das Objekt ist ebenfalls Teil des Projekts. Voraussetzung für die Umsetzung der kleinen Übung sind Visual Studio (es kann auch eine ältere Version sein). Ich empfehle die aktuelle Version Visual Studio 2015 Community Edition. Auch wenn es im Folgenden um richtige Programmierung in der Programmiersprache C+ geht, muss man kein Profi-Entwickler sein, um die Umsetzung selber durchführen zu können. Theoretisch könnte man das kleine Projekt auch komplett in der PowerShell ISE umsetzen, doch müsste man dann auf den wertvollen C#-Debugger verzichten.

Schritt 1: Erstellen der Typenkonverter-Assembly

Im ersten Schritt wird mit Visual Studio eine .Net-Assembly erstellt, in der eine Klasse sich von PSTypeConverter ableitet. Voraussetzung ist, dass ein Verweis auf System.Management.Automation eingebunden wurde was am einfachsten über den Paket-Manager geschieht. Dieser wird über das Tools-Menü und die Einträge Nuget Package Manager und Package Manager Console geöffnet. Ein Install-Package System.Management.Automation fügt die PowerShell-Assembly hinzu.

Die vorhandene Klassendatei Class1.cs wird in PSIniTypeConverter.cs umbenannt und der folgende Befehlscode eingefügt:

Die Klasse enthält relativ wenige Befehle, da die eigentliche Konvertierung in der Klasse IniObject erledigt wird. Die wichtigste Methode ist ConvertFrom, denn hier wird die Konvertierung angestoßen. Die Abfrage auf den Typ des Parameters destinationType wäre in diesem Beispiel nicht erforderlich. Ich habe sie eingebaut damit deutlich wird, dass sich auch mehrere Typen konvertieren lassen.

Schritt 2: Ein neuer Typ wird definiert

Im nächsten Schritt wird eine weitere Klasse eingefügt mit dem Namen IniObject.cs. Diese Klasse definiert den Typen, in den ein String konvertiert werden soll. Die Klasse besitzt den folgenden Inhalt.

Damit ist die Programmierung bereits abgeschlossen. Das Projekt sollte ich zum jetzigen Zeitpunkt ohne Fehler erstellen lassen. Das Ergebnis ist eine Assembly-Datei mit dem Namen PSIniTypeConverter.dll.

Schritt 3: Die Assembly soll in einem PowerShell-Skript geladen werden

Um die PowerShell-Typenkonvertierung per F5-Taste im Rahmen von Visual Studio testen zu können, wird in den Projekteigenschaften im Register Debug dafür gesorgt, dass mit dem Projektstart Powershell.exe gestartet wird. Dazu muss unter „Start external programm“ der vollständige Pfad von Powershell.exe eingetragen werden.

In den Projekteigenschaften wird eingestellt, dass mit dem Projektstart die PowerShell startet

In den Projekteigenschaften wird eingestellt, dass mit dem Projektstart die PowerShell startet

über die „Command line arguments“ wird ein kleines PowerShell-Skript mit dem Namen „Init.ps1“ gestartet, das dem Projekt hinzuzgefügt wurde (dank der genialen PowerShell Tools for Visual Studio von Doug Finke gibt es eine Vorlage für Ps1-Dateien mit Intellisense), und über das u.a. die Assembly geladen wird:

Per „-noexit“ wird erreicht, dass das Konsolenfenster geöffnet bleibt. Da in diesem Fall ein Profilskript ausgeführt werden soll, fehlt der Schalter „-Noprofile“ (in der Regel ist es sinnvoll, dass keine Profilskripte gestartet werden).

Das PowerShell-Skript ist wie folgt aufgebaut:

Zuerst wird die Assemblydatei und damit der Typenkonverter per Import-Module geladen. Anschließend werden der Typ, der eine Ini-Datei repräsentieren soll, und der Typenkonverter über eine Typendefinitionsdatei bekannt gemacht (mehr dazu gleich). Außerdem wird ein Typenalias mit dem Namen „IniObject“ definiert, damit bei der Typenkonvertierung nicht jedes Mal der Namespace vorangestellt werden muss. Außerdem wird für einen ersten Test eine Variable IniText vordefiniert, die den Inhalt einer Ini-Datei darstellen soll.

Schritt 4: Der Typenkonvertierer wird bekannt gemacht


Damit die Powershell sowohl den neuen Typ PSIniTypeConverter.IniObject als auch den Typenkonvertierer PSIniTypeConverter.IniTypeConverter kennt, werden diese über eine Typendefinitionsdatei bekannt gemacht. Diese heißt IniObject.Types.ps1xml, wird im Rahmen des Skripts über das Update-TypeData-Cmdlet geladen. Die Typendefinitionsdatei ist wie folgt aufgebaut:

Wird das Projekt jetzt per F5 gestartet, wird die PowerShell-Konsole gestartet und das Skript wird ausgeführt. Außerdem liegt bereits eine Variable IniText vor.

Das Visual Studio-Projekt wurde erfolgreich erstellt

Das Visual Studio-Projekt wurde erfolgreich erstellt

Der folgende Aufruf sollte eine Typenkonvertierung von String nach IniObject durchführen.

Das Beispielprojekt gibt es unter der folgenden Adresse: psinitypeconverter.

Zusammenfassung

PowerShell-Typenkonvertierer sind eine innovative Idee, die die Übernahme von Textdaten, die einem beliebigen Format vorliegen können, vereinfachen. Mit einem einzigen Typenalias kann eine komplette Textdatei in PowerShell-Objekte konvertiert werden. Ohne diese Option müsste man die Daten eventuell Zeile für Zeile einlesen, zerlegen und daraus Objekte machen. Ob sich der Aufwand für eigene Typenkonvertierer wirklich lohnt sei einmal dahin gestellt. Da es für wichtige Datentypen, etwa IP-Adressen, bereits Konvertierer gibt, dürfte sich die Anzahl der echten Anwendungsfälle in Grenzen halten.

Das kleine Beispiel, das ich in diesem Blogpost vorgestellt habe, soll in erster Linie dazu dienen ein wichtiges Merkmnal der PowerShell, das sie seit der Version 1.0 besitzt, die dynamische Typenkonvertierung, besser nachvollziehen zu können.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.