Welche Cmdlets besitzen keinen Alias? (oder die enorm nützliche HashTable im Einsatz)

Neulich in einem PowerShell-Seminar fiel mir zu einer von mir selbst gestellten Übungsaufgabe am Ende des Kurses als es darum ging, die Lösungen vorzustellen, auf die Schnelle (es war Viertel vor 5 und um 17 Uhr 21 ging der Bus zum Flughafen) keine Lösung ein, was auch daran lag, dass die Lösung etwas kniffliger war als sie zunächst bei der harmlosen Aufgabenstellung erschien. Die Aufgabe lautete wie folgt:

Liste alle Cmdlets auf, für die es keinen Alias gibt.

Also irgendwie eine Kombination aus get-command und get-alias und zwar möglichst kompakt (also ohne dafür ein umfangreiches Skript anzufertigen). Doch wie soll die Ausgaben der beiden Cmdlets so kombiniert werden, dass sich eine Art Teilmenge bilden lässt? Der entscheidende Tipp kam von einem Teilnehmer – wie wäre es mit einer HashTable?

Gute Idee, denn damit lässt sich fast jedes (PowerShell-) Problem deutlich einfacher lösen. Zuerst werden die Namen aller Cmdlets ,für die es einen Alias gibt, in eine HashTable geladen, wobei es nur darum geht, dass jeder Cmdlet-Name als Schlüssel  für den Eintrag eingesetzt wird, der Wert aber keine Rolle spielt. Anschließend listet ein get-command alle Cmdlets auf und es wird über die ContainsKey-Methode der HashTable geprüft, ob der Name des Cmdlets als Schlüssel in der HashTable vorkommt.

Hier ist eine mögliche Lösung:

get-alias | sort-object Definition -unique | foreach { $AliasCmd += @{$_.Definition = $Null}};
get-command  | where { !$AliasCmd.ContainsKey($_.Name)}

Der erste Befehl liefert die Namen aller Cmdlets (da es für ein Cmdlet mehrere Aliase geben kann, werden durch sort-object die Dupletten entfernt) und fügt in der foreach-Wiederholung jeden Namen zur Hashtable $AliasCmd, die aus dem „Nichts“ heraus gebildet wird. Als Wert für einen Eintrag wird $Null eingetragen, da dieser keine Rolle spielt. Der zweite Befehl durchläuft die Collection und prüft bei jedem Durchlauf, ob es den Namen als Key in der HashTable gibt.

Eine kompakte Lösung, die einmal mehr zeigt wie flexibel die PowerShell eingesetzt werden kann (mich würde brennend interessieren, ob es nicht noch eine kompaktere Lösung gibt).

Für einen Grundlagenkurs ist sie allerdings doch ein wenig zu anspruchsvoll, doch man lernt bekanntlich am besten an Herausforderungen. Und es ist erstaunlich für wieviele Cmdlets es keinen Alias gibt.

Nachtrag:

Kurz nach der Schulung ereichten mich zwei Lösungsvorschläge eines Teilnehmers, dem die ungelöste Aufgabe keine Ruhe gelassen hatte.

Variante 1: Kurz, aber ein wenig langsam.

get-command |where {(@(get-alias |select definition) -match $_.name).count -eq 0}

Diese Variante ist in der Tat etwas langsam, da für jedes Cmdlet ein Aufruf von get-alias durchgeführt wird.

Variante 2: Schnell und dank Match-Operator etwas elegangter als per HashTable:

$ac=@(get-alias |select definition)

get-command | where {($ac -match $_.name).count -eq 0}

Diese Variante gefällt mir ein wenig besser als meine Lösung, da man nichts über eine HashTable wissen muss und der Match-Operator im Kurs auch vorgestellt wurde (die HashTable wurde lediglich nach dem Motto „das ist eher etwas für den Fortgeschrittenen Kurs“ vorgestellt).

Man könnte lediglich den ersten Befehl etwas „optimieren“:

$ac=@(get-alias |select definition | sort definition -unique)

Was kann es Schöneres geben, als wenn die „Schüler“ nach dem Kurs auf bessere Lösungen kommen als der Lehrer.

Schreibe einen Kommentar

Bitte logge dich mit einer dieser Methoden ein, um deinen Kommentar zu veröffentlichen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s