Windows PowerShellDie Drop Box

Don Jones

Inhalt

Cmdlets in Aktion
Die Filterfunktion
Physisches Denken
Praktische Anwendungen
Zeit zum Spielen

In einer meiner letzten Windows PowerShell-Schulungen hatten einige Studenten Schwierigkeiten, sich das Konzept der Pipeline in der Shell vorzustellen. Zugegeben, die Pipeline ist vom Konzept her nicht ganz intuitiv, und deshalb kann es für visuell Lernende ziemlich schwierig sein, sich die einzelnen Vorgänge vorzustellen. Als das

Konzept der Filterfunktionen vorgestellt wurde, die direkt in der Pipeline arbeiten, wurde es richtig kompliziert, und es stand förmlich in ihren Gesichtern geschrieben, dass einige nicht mehr folgen konnten.

Um den Studenten zu helfen, brachte ich am nächsten Tag einen Pappkarton, einige Namensschildaufkleber und ein paar Tischtennisbälle mit. (Windows PowerShell® kann doch schließlich Spaß machen.) Für eine Demonstration verwendete ich die folgende Befehlszeile:

Get-Process | Where { $_.VM –gt 1000 } | Sort VM
–descending | Export-CSV C:\Procs.csv

Cmdlets in Aktion

Mithilfe der Namensschildaufkleber (Sie wissen schon, die Aufkleber von der Sorte „Hallo, Mein Name ist …“, die Sie immer bei Klassentreffen und ähnlich öden Veranstaltungen tragen müssen) wurde jedem Studenten ein Cmdlet-Name zugewiesen. Die Tischtennisbälle sollten Windows®-Prozessobjekte repräsentieren (insbesondere Objekte vom Typ „System.Diagnostics.Process“). Die Studenten sollten mir mitteilen, welcher der Cmdlet-Namen sich so anhört, als würde er wahrscheinlich Prozessobjekte generieren.

Nach Ansicht aller Schilder entschieden sich die Teilnehmer für den offensichtlichen Namen „Get-Process“. Dies ist einer der Hauptgründe, warum mir die Cmdlet-Namen gefallen, die in Windows PowerShell verwendet werden: sie sind meist offensichtlich.

Daher nahm sich der Student, der Get-Process repräsentierte, alle Tischtennisbälle und warf sie in den Pappkarton. Der Karton repräsentierte die Pipeline der Shell, und der Student führte ziemlich genau das aus, was auch ein Cmdlet vornimmt. Ein Cmdlet generiert ein oder mehrere Objekte und steckt sie dann in die Pipeline.

Sie werden dann vom nächsten Cmdlet in der Pipeline übernommen. In diesem Beispiel nimmt sich der Student, der das Cmdlet „Where-Object“ repräsentiert, alle Tischtennisbälle und überprüft für jeden einzelnen, ob die VM-Eigenschaft eines bestimmten Balls größer als 1.000 ist. In der Shell steht „VM“ für den virtuellen Arbeitspeicher, und für diese Übung wurde die jeweilige Größe des virtuellen Arbeitsspeichers mit einem Marker auf jeden Tischtennisball geschrieben.

Jeder Ball (auch bekannt als Prozess) mit einem VM von mindestens 1.000 ging zurück in den Karton (die Pipeline). Alle Bälle mit einem kleineren Wert wurden für immer in einen Papierkorb gelegt. (Das ist in Wirklichkeit nicht ganz richtig – ich habe sie nämlich für eine zukünftige Lektion wieder eingesammelt.)

Als Nächstes ging der Student, der vorgab, das Sort-Objekt zu sein, durch den Karton mit den Tischtennisbällen und sortierte sie anhand ihrer VM-Eigenschaft. Zugegeben, dies war der Teil der Übung, der nicht so gut durchdacht war: Es war ziemlich schwierig, die Bälle am Wegrollen zu hindern! Vermutlich sind für das nächste Seminar Tischtenniswürfel oder Eierkartons besser geeignet.

Zum Schluss sammelte der Student, der die Rolle der Export-CSV übernommen hatte, die Bälle auf und übertrug die Informationen in eine CSV-Datei. Durch Notieren der Prozesseigenschaften auf ein Flipchart (das für die CSV-Datei stehen sollte), wurde der Vorgang physisch dargestellt.

Die Filterfunktion

Nach Erörtern der einfachen Pipeline waren die Filterfunktionen an der Reihe, ein etwas komplizierteres Thema. Dazu sollte die in Abbildung 1 gezeigte Filterfunktion und Befehlszeile verwendet werden.

Abbildung 1 Beispiel für eine Filterfunktion und Befehlszeile

Function Do-Something {
 BEGIN { }
 PROCESS {
  $obj = New-Object PSObject
  $obj | Add-Member NoteProperty "TimeStamp" (Get-Date)
  $obj | Add-Member NoteProperty "ProcessName" ($_.Name)
  Write-Output $obj
 }
 END {}
}
Get-Process | Do-Something | Format-Table *

Zunächst folgt ein kurzer Überblick über die Funktionsweise von Filterfunktionen. Eine Funktion enthält drei Skriptblöcke mit den Namen BEGIN, PROCESS und END.

Bei Verwendung der Funktion in der Pipeline wird als Erstes der Skriptblock BEGIN ausgeführt. Dieses Skript führt beliebige Setupaufgaben durch, z. B. das Öffnen einer Datenbankverbindung.

Dann wird der Skriptblock PROCESS ein Mal für jedes Objekt ausgeführt, das an die Funktion weitergeleitet wurde. Innerhalb des Skriptblocks PROCESS wird die $_-Variable automatisch mit dem aktuellen Pipelineobjekt belegt. Wenn also 10 Objekte an die Pipeline gesendet werden, wird der Skriptblock PROCESS 10 Mal ausgeführt.

Nach Ausführung aller an die Pipeline gesendeten Objekte wird der Skriptblock END ausgeführt und alle erforderlichen Bereinigungsaufgaben vorgenommen, z. B. das Schließen einer Datenbankverbindung.

Alles, was innerhalb dieser Skriptblöcke mithilfe von Write-Output geschrieben wird, geht in der Pipeline an das nächste Cmdlet weiter. Alles, was nicht mithilfe von Write-Output geschrieben wird, wird verworfen.

Da der Befehl mit einem Get-Process startete, sammelte der obige Student alle Tischtennisbälle ein (wir machten eine kurze Pause, und aus irgendeinem unerfindlichen Grund waren danach die Bälle im ganzen Raum verteilt) und legte sie in den Pipelinekarton. Nach Abschluss dieser Aufgabe war der Student, der die Filterfunktion „Do-Something“ repräsentierte, an der Reihe.

Er führte als Erstes seinen Skriptblock BEGIN aus. Da der Skriptblock leer war, war das nicht allzu anstrengend. Als Nächstes nahm er sich alle Pipelineobjekte (die Tischtennisbälle) zur Hand und sah sie sich einen nach dem anderen an. Das war ziemlich witzig, denn er versuchte, alle Bälle – mehr als ein Dutzend – auf dem Schoß zu balancieren.

Jedenfalls nahm er jeweils ein Prozessobjekt auf und führte den Skriptblock PROCESS aus. Dieser Skriptblock ermöglichte ihm das Erstellen eines vollständig neuen benutzerdefinierten Objekts. (Dafür wurden spezielle gelbe Tischtennisbälle verwendet, und glauben Sie bloß nicht, dass die in den lokalen Sportläden besonders leicht zu finden waren.) Auf diese neuen Tischtennisbälle schrieb er das aktuelle Datum und die Uhrzeit sowie den Namen des Prozessobjekts, das er sich gerade ansah.

Dieses neue benutzerdefinierte Objekt wurde dann in die Pipeline geschrieben, das heißt, der gelbe Tischtennisball wurde im Karton platziert und der ursprüngliche Tischtennisball des Prozessobjekts verworfen. Der Student wiederholte dies für jeden Prozessobjekt-Tischtennisball im Karton, also etwa 12 Mal. Am Ende hatte er jeden weißen Tischtennisball durch einen gelben benutzerdefinierten Objekt-Tischtennisball ersetzt. Zum Schluss führte er den Skriptblock END aus, wofür so gut wie keine Zeit benötigt wurde, da er leer war.

Als Letztes übernahm der Format-Table-Student. Er nahm alle Bälle im Karton (das waren zu diesem Zeitpunkt alle gelben „benutzerdefinierten“ Objekte) und begann mit dem Erstellen einer Tabelle, wobei er die beiden Eigenschaften verwendete, die auf jedem Ball notiert waren. Auf dem Flipchart entstand eine Tabelle mit zwei Spalten (Zeitstempel und ProcessName) und etwa einem Dutzend Zeilen.

Physisches Denken

Erst durch diese Übung wurden die Pipeline und die Filterfunktion wirklich jedem in der Klasse klar. Mithilfe der Tischtennisbälle wurde die Darstellung der Objekte wirklich gut veranschaulicht. Diese Darstellung kann ziemlich abstrakt werden, wenn Sie nur über die Shell reden.

Jeder konnte sehen, wie die Cmdlets die Objekte änderten, wie die Ergebnisse in die Pipeline platziert wurden und wie das nächste Cmdlet die Ergebnisse aufnahm und die Objekte weiter änderte. Der Ablauf der Ereignisse in einer Filterfunktion sowie die Arbeitsweise des Skriptblocks PROCESS, jeweils ein Eingabeobjekt gleichzeitig zu bearbeiten, wurden verdeutlicht.

Weiterhin wurde die Art und Weise aufgezeigt, wie ein neues benutzerdefiniertes Objekt erstellt und in der Pipeline platziert werden kann. Außerdem wurden die Vorteile benutzerdefinierter Objekte im Gegensatz zu reinem Text demonstriert. Die neuen benutzerdefinierten Objekte konnten von anderen Cmdlets genutzt werden, z. B. von Format-Table. Dadurch können die Daten anschließend auf flexiblere Weise verwendet und dargestellt werden.

Praktische Anwendungen

Die wichtigste Frage der Studenten war, wie sich die Pipeline und die Filterfunktionen für praktische Anwendungen nutzen lassen. Die Antwort fiel mir nicht schwer, da ich bereits mehrere Beispiele für eine frühere Konferenz vorbereitet hatte (Sie finden die Beispiele unter scriptinganswers.com/essentials/index.php/2008/03/25/techmentor-2008-san-francisco-auditing-examples).

Das Ziel bei einem der Beispiele war, von mehreren Computern mehrere Eigenschaften der Win32_UserAccount-Klasse in der Windows-Verwaltungsinstrumentation (Windows Management Instrumentation, WMI) aufzulisten. Wenn die Computernamen in „C:\Computers.txt“ aufgelistet sind, können Sie diesen einfachen Befehl verwenden:

Gwmi win32_useraccount –comp (gc c:\computers.txt)

Das Problem besteht darin, dass die Win32_UserAccount-Klasse keine Eigenschaft enthält, aus der ersichtlich ist, welche Instanz von welchem Computer stammt. Daher ist die sich ergebende Liste eine nutzlose Mischung an Konten von vielen Computern. Das Problem wurde durch Erstellen eines neuen benutzerdefinierten Objekts gelöst, das den ursprünglichen Computernamen sowie die ausgewählten Klasseneigenschaften enthält, die hier wichtig sind. Der Code für dieses benutzerdefinierte Objekt wird in Abbildung 2 gezeigt.

Abbildung 2 Verwenden eines benutzerdefinierten Objekts zum Sammeln von Computernamen und Auswahl von Eigenschaften

function Get-UserInventory {
  PROCESS {
    # $_ is a computer name
    $users = gwmi win32_useraccount -ComputerName $_
    foreach ($user in $users) {
      $obj = New-Object
      $obj | Add-Member NoteProperty Computer $_
      $obj | Add-Member NotePropertyPasswordExpires ($user.PasswordExpires)
      $obj | Add-Member NoteProperty Disabled ($user.Disabled)
      $obj | Add-Member NotePropertyFullName ($user.FullName)
      $obj | Add-Member NoteProperty Lockout ($user.Lockout)
      $obj | Add-Member NoteProperty Name ($user.Name)
      $obj | Add-Member NotePropertyPasswordRequired ($user.PasswordRequired)
      $obj | Add-Member NotePropertyPasswordChangeable ($user.PasswordChangeable)
    }
  }
}

Get-Content c:\computers.txt | 
  Get-UserInventory | 
  where { $_.PasswordRequired -eq $False } | 
  selectComputer,Name | 
  Export-Csv c:\BasUsersBad.csv

Die endgültige Befehlszeile übergibt alle Computernamen an die Filterfunktion, die dann die benutzerdefinierten Objekte erzeugt. Als Nächstes wurden alle Benutzer außer denen herausgefiltert, bei denen die PasswordRequired-Eigenschaft auf „False“ festgelegt wurde (es sollte nämlich ein Prüfbericht der problematischen Konten erstellt werden). Dann wurden ganz einfach die Computer- und die Name-Eigenschaften beibehalten. Der Abschlussbericht besteht somit aus einer Liste von Computer- und Kontennamen, die näher untersucht werden müssen, weil die Kennwörter niemals ablaufen. Die Filterfunktion ermöglicht einen solchen Bericht über mehrere Computer hinweg, da der ursprüngliche Computername der Ausgabe hinzugefügt wird, während die Eigenschaften gleichzeitig auf die wichtigsten reduziert werden.

Obwohl es vergleichbare Möglichkeiten zum Ausführen dieser Aufgabe gibt, ist dieser Ansatz wohl der einfachste. Außerdem werden dadurch auch wichtige Konzepte und Verfahren demonstriert.

Zeit zum Spielen

Selbst wenn Sie mit der Pipeline vertraut sind, können Sie etwas aus diesem Artikel lernen. Durch Übertragen Ihrer Denkweise auf physische Objekte lassen sich Vorgehensweisen oft verdeutlichen.

Wenn Sie also beim nächsten Mal Schwierigkeiten mit einem Windows PowerShell-Konzept haben, gehen Sie vom Computer weg, und stellen Sie die Aufgabe als Beispiel mithilfe alltäglicher Gegenstände dar. Halten Sie zu diesem Zweck immer ein paar Tischtennisbälle bereit (oder auch Würfel, wenn Sie sie irgendwo finden).

Cmdlet des Monats: Out-File

Wie oft schon haben Sie die Ausgabe eines Cmdlet mithilfe des Symbols „>“ in eine Datei umgeleitet? Dieses Verfahren ähnelt „Get-Process > Processes.txt“. Wissen Sie, dass sich dahinter eigentlich das Out-File-Cmdlet verbirgt? Mit dem folgenden Befehl wird genau das Gleiche ausgeführt: Get-Process | Out-File Processes.txt.

Natürlich erfordert dies mehr Tipparbeit, warum also sollten Sie das vollständige Out-File-Cmdlet eingeben? Ein Grund ist, dass sich Out-File erheblich besser lesen lässt. In 6 Monaten schaut sich vielleicht jemand Ihr Skript an und fragt sich, wofür das „>“ steht (es handelt sich dabei irgendwie um eine Art Altlast).

Im Gegensatz dazu wird durch Out-File ziemlich offensichtlich, dass eine Datei erstellt und in sie geschrieben werden soll. Außerdem erhalten Sie durch Out-File den -append-Parameter (stimmt ziemlich genau mit „>>“ überein) sowie die Parameter -force und -noClobber, mit denen das Überschreiben vorhandener Dateien gesteuert werden kann. Schließlich erhalten Sie durch Ausschreiben von Out-File auch Zugriff auf den Parameter -whatif. Dieser nützliche Parameter zeigt Ihnen die Auswirkungen von Out-File, ohne dass der Vorgang tatsächlich durchgeführt wird! Das ist eine großartige Möglichkeit, eine komplizierte Befehlszeile möglichst gefahrlos zu testen.

Don Jones ist Mitautor von Windows PowerShell: TFM und bietet Schulungen zu Windows PowerShell an (www.ScriptingTraining.com).

© 2008 Microsoft Corporation und CMP Media, LLC. Alle Rechte vorbehalten. Die nicht genehmigte teilweise oder vollständige Vervielfältigung ist nicht zulässig.