Archiv für den Monat: September 2018

Convert a hashtable into its string representation

Recently I had to find a technique to update a module manitest file (psd1). Using the Update-ModuleManifest cmdlet is the official way to achieve this. Although using this command is really simple it completely overwrites the current psd1 file and inserts all the „junk“ that I want to keep out from that file.

Another approach is to load the psd1 file with the Import-LocalizedData cmdlet which results to a hashtable.

Updating single entries is easy:

But how do you turn the the whole hashtable with all its keys and values into a string again? Not so easy. Luckily I found an old blog entry by June Blender (when she was still working for Sapien Technologies):

https://www.sapien.com/blog/2014/10/21/a-better-tostring-method-for-hash-tables/

But June restricted her function to simple hashtables with no values that are hashtables themselves.

So I have extend her function so that it works also with hashtable that contains other hashtable values which is necessary to convert a hashtable that represents a psd1 file. Ny function calls itself recursively which which is not hard to accomplish in PowerShell. The difficult part can be to find an exit condition or threat the last value differently. For this reason I am not using foreach but the for command with an index variable to enumerate all the values. For the last value I add „}“ to complete the hashtable string.

As PowerShell expert Kirk Munro suggested in his comments to Junes blog post converting the hashtable to json might be a simpler solution. I have not tried this yet although it probably could have saved me a lot of typing and trying. But hey, why choosing a simple solution if you can spend a couple of hours fiddling with Powershell script code;)

So this should work. I have tested the function with a specific psd1 file. If not please leave a comment or send me a mail.

PowerShell Core 6.1 mit interessanten Neuerungen

Am Anfang fand ich die Idee gut, dann war ich etwas skeptisch, inzwischen bin aber überzeugt, dass PowerShell Core die Zukunft der PowerShell ist. Seit einigen Monaten arbeite ich nahezu ausschließlich mit PowerShell Core was mir insofern leicht fällt, da ich kein Administrator bin, der beruflich die PowerSHell einsetzt und damit auf Module angewwiesen ist, die sich unter PowerShell Code nicht nutzen lassen, wenngleich dies nur noch auf wenige Module zutreffen dürfte.

Im Windows-Umfeld bringt PowerShell Core nur wenige Vorteile. Im Gegenteil, bedingt durch den Umstand, dass „Core“ bedeutet, dass PowerShell Code auf .NET Core und nicht .NET Framework basiert und und in .NET Core 2.0 einige APIs fehlten, die von PowerShell-Commands vorausgesetzt werden, waren bestimmte Aufrufe, etwa WMI, einfach nicht möglich. Unter Windows ist das natürlich kein Thema, denn man bleibt ganz einfach bei der Windows PowerShell.

Microsoft wäre nicht Microsoft, wenn es seine Anmwender/Kunden nicht zu irgendetwas „zwingen“ würde. Wir sollen alle in die Cloud gehen, also gibt es bei Windows Server mit jeder Version weniger Innovationen (wenngleich auch Windows Server 2019 interessante Neuerungen enthält). Da in Zukunft nur noch PowerShell Core weiterentwickelt wird, sollen Administratoren in Zukunft eher mit diseser Version arbeiten. Da bei .NET Core in der Vergangenheit wichtige APis fehlten, die es nur unter Windows gibt, wurden diese in Gestalt der „Compatibilty Packs“ inzwischen nachgereicht. Damit ist auch bei PowerShell Core z.B. WMI wieder möglich. Natürlich nur unter Windows.

Trotzdem fallen mir nur wenige Gründe ein, warum ein Administrator unter Windows anstelle der Windows PowerShell PowerShell Core verwenden sollte.

Nachdem im Januar 2018 die Version 6.0 freigegeben wurde, erschien im September 2018, also etwa mehr als ein halbes Jahr später, die Version 6.1.

Sie wird im Blog des PowerShell-Teams ausführlich vorgestellt:

https://blogs.msdn.microsoft.com/powershell/2018/09/13/announcing-powershell-core-6-1/

und vorallem

https://docs.microsoft.com/en-us/powershell/scripting/whats-new/what-s-new-in-powershell-core-61?view=powershell-6

Der Vollständigkeit halber habe ich die wichtigsten Neuerungen noch einmal stichwortartig zusammengefasst:

>Umstellung auf .NET Core 2.1
>Performance-Verbesserungen, z.B. bei Import-CSV
>Über die Eigenschaft PSEdition lässt sich bei Modulen abfragen, ob wie für Core, Desktop oder beide in Frage kommen. Diese Information muss aber in die Manifestdatei eingetragen werden, damit sie abgefragt werden kann
>Es gibt Cmdlets für den Umgang mit Markdown (sehr praktisch)
>Das Invoke-WebRequest-Cmdlet wurde verbessert
>Das Enable-PSSession-Cmdlet wurde verbessert
>Ein cd – führt zum letzten Verzeichnis zurück
>Ein cd– führt in das $Home-Verzeichnnis
>Für Update-Help muss die PowerShell nicht mehr als Administrator gestartet werden
>Ein [PSCustomObject]-Objekt besitzt jetzt eine Length- und Count-Eigenschaft

Bemerkenswert und im Grunde naheliegend ist, dass einige der Verbesserungen aus der Community stammen.

Tipps für Zwischendurch: Fenster auflisten mit der EnumWindows-API-Funktion

Vor kurzem fand ich nach der Suche nach einer Lösung auf Stackoverflow einen Link auf ein PowerShell-Skript, das ich vor vielen Jahren erstellt hatte. Leider hatte der Autor meinen Namen nicht erwähnt, sonst wäre ich bei Stackoverflow zu unverhofftem Ruhm gekommen. Außerdem verwies der Autor des Beitrags auf meinen alten Blog powershell-knowhow.de, den es schon länger nicht mehr gibt. Da ich das Skript von damals auf diversen Festplatten nicht wiederfinden konnte, musste ich mich noch einmal an die Arbeit machen. Wie sooft war das Ergebnis besser als das Original, da es deutlich kürzer geworden ist. Das ist generell ein Erfahrungswert, den ich aus über 10 Jahren PowerShell ziehen kann, und der vermutlich universell gilt. Um auf die kürzeste Lösung zu kommen, muss man sich in den Bereich, in dem die Lösung gesucht wird, bereits ziemlich gut auskennen.

Wie dem auch sei, die folgende Function Get-WindowData gibt Fensterhandle und Text zu jedem Top-Window-Fenster aus (und das sind bereits erstaunlich viele). Theoretisch lassen sich durch eine Art rekursiven Aufruf natürlich auch die Kindfenster eines Fensters auflisten, aber diese Mühe habe ich mir dann doch nicht gemacht.

Interesant und lehrreich an dem Beispiel ist die Leichtigkeit, mit der sich ein „Callback“, also eine Adresse auf eine Funktion, die von der API-Funktion pro Fenster aufgerufen wird, von PowerShell an eine Win32-API-Funktion übergeben lässt. Merkregel: Überall, wo ein Delegate erwartet wird, darf auch ein Scriptblock übergeben werden.

Kleine Tipps für Zwischendurch: Kopfzeile und restliche Zeilen einer Textdatei mit einem Befehl getrennt einlesen

Bei einer Textdatei sollen Kopfzeile und die restlichen Zeilen getrennt eingelesen werden. Aber mit einem einzigen Befehl.

Mit folgender Konstruktion ist mir das gelungen.

Zunächst ein paar Testdaten als Here-String:

Dann der Befehl, der die Textzeilen in eine erste Zeile und die restlichen Zeilen aufsplittet und das Ergebnis zwei Variablen zuweist:

Warum verwende ich kein ForEach-Object? In erster Linie weil ich es etwas „übertrieben“ finde, wenn ein Wiederholungsbefehl für einen einzigen Verarbeitungschritt verwendet werden muss. Auch wollte ich keine Variable verwenden. Dann wäre auch Folgendes möglich:

Die Variable Zeilen wird gebraucht, da ich nur so an die Länge des Array beim Zugriff auf dasselbige herankomme.

Es soll direkt nach dem Schema Textzeilen | Verarbeitung => Ergebnis funktionieren. Ohne Zwischenschritte und Variablen.

Was nicht jeder wissen dürfte, auch eim harmloser Scriptblock kann in einen Begin-, Process- und End-Block unterteilt werden. Möchte man in einen Scriptblock etwas pipen, geht das nur mit eimem vorangestellten &, das den Scriptblock ausführt.

Tja, was soll man davon halten? Es funktioniert, ok. Ist das Ganze intuitiv nachvollziehbar? Eher weniger. Auf der anderen Seite, im Vergleich zu einem Befehl in Bash oder Perl, der zum selben Resultat führt, ist die PowerShell-Variante regelrecht leicht verdaulich, denn es kommen weder reguläre Ausdrücke noch kryptische Variablen wie %%S vor;)

Tipps für Zwischendurch: COM-Methoden mit Enum-Werten aufrufen

Dieser Tipp ist etwas spezieller.

Ausgangspunkt ist eine COM-Dll (früher OLE Server oder COM Server genannt), die eine Methode Calculate anbietet, die drei Parameter besitzt. Der erste Parameter ist vom Typ eines Enum mit dem Namen „OpType“, die anderen beiden Parameter sind vom Typ int.

Das Anlegen eines COM-Objekts mit der ProgId ist einfach:

Der Aufruf von Calculate funktioniert zunächst nur, wenn für den Enum-Wert eine Zahl übergeben, z.B. eine 0.

Der Enum OpType ist zunächst nicht bekannt. Ein „Trick“ besteht darin, ihn per Enum-Befehl nachzubauen, den es ab Version 5.0 gibt.

Damit klappt auch der folgende Aufruf:

Tipps für Zwischendurch: Registry-Suche – besser ohne PowerShell

Das Durchsuchen der Registry ist nicht gerade die Stärke der PowerShell. Einfacher geht es mit dem reg-Kommando, dessen Syntax zudem übersichtlich per /? abrufbar ist.

Hier eine Kostprobe. Gesucht sind alle Einträge, die ein „MathLibV1“ enthalten.

Per PowerShell wäre eine solche Suche deutlich umständlicher und vor allem langsamer. Das beginnt bereits mit dem Umstand, dass es keinen Befehl gibt, der die Registry nach Werten durchsucht. Das mit Version 5.0 dazu gekommende Get-ItemPropertyValue-Cmdlet klingt praktisch, ist aber sehr limitiert, da der Name-Parameter keine Platzhalter akzeptiert.

Geht es nur um eine schnelle Suche, ist reg einfach unschlagbar.