Windows PowerShell Es sieht gut aus

Don Jones

Häufig sollen mit Skripts ansprechend formatierte Dokumente erstellt werden, und ich werde oft gefragt, wie man beispielsweise Tabellen am besten erstellen kann. Es gibt hierfür zwei Möglichkeiten. Der erste Schritt besteht darin, ein Skript zu schreiben, das die Daten, die ich formatieren möchte, in Variablen mit den Namen

$data1, $data2 und $data3 speichert. Anschließend kann ich die Daten formatieren.

Die Textmethode

Im Web

Sie möchten sich die in diesem Monat im Windows PowerShell-Artikel erklärten Verfahren schnell demonstrieren lassen? Gehen Sie zu technetmagazine.com/video, um zu sehen, wie Don Jones diese Cmdlets umsetzt.

In der Regel gehen Programmierer bei dieser Aufgabe genauso vor, wie sie es bei anderen Sprachen wie VBScript, Perl, KiXtart oder einer anderen textorientierten Sprache tun würden. Vielleicht werden im ersten Schritt Spaltenüberschriften auf den Bildschirm geschrieben:

Write-Host "ColumnA'tColumnB'tColumnC"

Beachten Sie, dass das `t eine spezielle Escapesequenz in Windows PowerShellTM ist, die ein Tabstoppzeichen einfügt. Wenn ich jede Zeile meiner Tabelle ausschreiben will (meine Daten sind in $data1, $data2 und $data3), habe ich hierfür mehrere Möglichkeiten. Eine besteht darin, einfach die Variablen zu schreiben und mich darauf zu verlassen, dass die Shell Variable durch ihre Inhalte in doppelten Anführungszeichen ersetzt:

Write-Host "$data1't$data2't$data3"

Eine andere, etwas bessere Methode besteht darin, hierfür den –f-Operator zu verwenden. Bei dieser Methode wird die Formatierung von den Daten getrennt, wodurch die Codezeilen meiner Meinung nach leichter lesbar und besser verwaltbar werden.

Write-Host "{0}'t{1}'t{3}" –f $data1,$data2,$data3

Bei dieser Methode ergeben sich allerdings große Probleme. Zunächst einmal wird, wenn ich mich auf die festen Tabstopps im Konsolenfenster verlasse, die Formatierung an einigen Stellen sehr merkwürdig aussehen. Wenn beispielsweise eine Tabellenzeile 20 Zeichen in der ersten Spalte enthält, bringe ich damit die ganze Formatierung für diese Zeile durcheinander. Außerdem bin ich dadurch, dass ich diesen Text mittels Write-Host ausgebe, mehr oder weniger auf eine Konsolenanzeige beschränkt.

Es gibt keine einfache Möglichkeit, diese Ausgabe an eine Datei zu senden oder sie in andere Formate umzuwandeln, falls ich das jemals tun möchte. Was aber am wichtigsten ist: Bei diesem textbasierten Ansatz wird die inhärent objektbasierte Shell, in der ich arbeite, vollständig ignoriert, und ich nutze damit nicht die leistungsstarken Verfahren und Features, die Windows PowerShell bietet.

Die Windows PowerShell-Funktionsweise

Windows PowerShell ist eine objektorientierte Shell. Das bedeutet, dass Sie im Idealfall nur in Objekten arbeiten, die es der Shell ermöglichen, etwas bei Bedarf in Textanzeigen umzuwandeln. Aber wie erstellen Sie Objekte für zufällige Datenfragmente?

In meinem obigen Beispiel beginne ich damit, ein leeres benutzerdefiniertes Objekt zu erstellen und es in einer Variablen zu speichern:

 $obj = New-Object PSObject

Hierdurch erhalte ich ein neues, leeres Objekt, mit dem ich arbeiten kann. Anschließend füge ich dem Objekt meine Daten in Form von Eigenschaften hinzu. Hierzu sende ich mein Objekt einfach zum Add-Member-Cmdlet. Ich füge eine NoteProperty hinzu, gebe der Eigenschaft einen Namen (es empfiehlt sich, als Eigenschaftsnamen die Spaltenüberschriften zu verwenden) und füge dann die Daten als Werte für die Eigenschaften ein:

 $obj | Add-Member NotePropertyColumnA $data1
$obj | Add-Member NotePropertyColumnB $data2
$obj | Add-Member NotePropertyColumnC $data3

Jetzt gebe ich einfach dieses Objekt aus, und zwar nicht an die Konsole, sondern an die Pipeline:

Write-Output $obj

Ich kann dann diese Schritte für jede Tabellenzeile wiederholen, die ich ausgeben muss. Die folgende kurze Funktion akzeptiert eine Zeichenfolge und gibt eine Version in Groß- und Kleinbuchstaben sowie die ursprünglichen Zeichenfolge aus:

functionStringVersions {
param([string]$inputString)
  $obj = New-Object PSObject
  $obj | Add-Member NoteProperty Original($inputString)
  $obj | Add-Member NoteProperty Uppercase($inputString.ToUpper())
  $obj | Add-Member NoteProperty Lowercase($inputString.ToLower())
  Write-Output $obj
}  
$strings = @("one","two","three")
foreach ($item in $strings) {
StringVersions $item
}

Ich fange mit einem Array von Zeichenfolgenvariablen an und sende diese nacheinander an die Funktion. Die Ausgabe der Funktion wird hierdurch in die Pipeline geschrieben.

Dieser Code könnte kompakter geschrieben werden, aber ich habe ihn extra so wie hier gezeigt geschrieben, weil hierdurch besser illustriert wird, was ich mit dem Code erreichen will. Das Ergebnis, das in Abbildung 1 zu sehen ist, ist eine perfekt formatierte Tabelle. Der Grund hierfür ist, dass die Shell bereits weiß, wie Objekte in einer Tabelle zu formatieren sind.

Abbildung 1 Windows PowerShell-Ausgabe in einer Tabelle

Abbildung 1 Windows PowerShell-Ausgabe in einer Tabelle(Zum Vergrößern auf das Bild klicken)

Wenn ein Objekt vier oder weniger Eigenschaften besitzt, wählt Windows PowerShell automatisch eine Tabelle aus. Ohne selbst hierfür etwas getan zu haben, habe ich jetzt eine schön formatierte Tabelle.

Das ist aber noch nicht alles.

Cmdlet des Monats: Get-Command

Bestimmt haben Sie schon einmal Get-Command oder seinen praktischen Alias (gcm) verwendet, um die Liste der verfügbaren Windows PowerShell-Cmdlets aufzurufen. Aber möglicherweise wissen Sie nicht, wie flexibel gcm wirklich ist. Wenn Sie zum Beispiel sehen möchten, was Windows PowerShell mit einem Dienst tun kann, führen Sie „gcm -noun service“ aus. Wenn Sie alle Windows PowerShell-Exportoptionen sehen möchten, versuchen Sie es mit „gcm -verb export“. Wenn Sie nur sehen wollen, welche Cmdlets von einem bestimmten Snap-In wie z. B. den PowerShell-Communityerweiterungen hinzugefügt wurden, versuchen Sie es mit „gcm -pssnapin pscx“. (Sie können „pscx“ durch einen beliebigen Snap-In-Namen ersetzen, um die Cmdlets des jeweiligen Snap-Ins zu sehen.)

Wie Sie sehen, ist Get-Command ein wichtiger Befehl, um den Funktionsumfang der Windows PowerShell vollständig nutzen zu können. Sie können sich auf diese Weise mit dem Funktionsumfang vertraut machen, ohne das Handbuch lesen zu müssen. Mit gcm lässt sich der Funktionsumfang zudem genauer erforschen als mit einem Befehl wie „Help *“. Die Help-Funktion listet nur verfügbare Hilfethemen auf. Cmdlets, die nicht zusammen mit einer Hilfe ausgeliefert wurden, tauchen nicht in der Hilfeliste auf, obwohl sie Ihnen zur Verfügung stehen, wenn Sie sie benötigen.

Der Vorteil dieser Methode geht weit über Tabellen hinaus. Wenn Sie mit Objekten arbeiten, bietet Windows PowerShell einen riesigen Funktionsumfang. Sie wollen Ihre Daten in eine CSV-Datei exportieren? Verwenden Sie Export-CSV. Sie bevorzugen eine HTML-Tabelle? Senden Sie Ihre Objekte an ConvertTo-HTML. Sie benötigen ein Listenformat? Senden Sie die Objekte an Format-List. Mit Objekten können Sie all die Funktionen nutzen, die Shell bereits kennt. Hier ist ein überarbeitetes Beispiel:

functionStringVersions {
  PROCESS {
   $obj = New-Object PSObject
   $obj | Add-Member NoteProperty Original($_)
   $obj | Add-Member NoteProperty Uppercase($_.ToUpper())
   $obj | Add-Member NoteProperty Lowercase($_.ToLower())
   Write-Output $obj
}
}

Dieses Mal habe ich die Funktion geändert, die die Pipelineeingabe akzeptiert (was immer besser ist, wenn Sie mit Objekten arbeiten) und die Objekte an die Pipeline ausgibt.

Der Code für diese Funktion befindet sich jetzt in einem PROCESS-Skriptblock. Das bedeutet, dass diese Funktion eine Pipelineeingabe akzeptiert und den PROCESS-Skriptblock einmal für jedes Objekt ausführt, das an die Pipeline gesendet wurde.

Innerhalb des PROCESS-Skriptblocks verweist die spezielle Variable $_ auf das das aktuelle Pipelineobjekt, das gerade verarbeitet wird. Das Praktische hierbei ist, dass ich jetzt einfach ein Zeichenfolgenarray an eine Pipeline senden kann:

@("one","two","three") | StringVersions

Ich erhalte eine Tabelle, weil die Funktion ihre Ausgabe an die Pipeline sendet. Am Ende der Pipeline weiß die Shell, dass sie ihr Formatierungssubsystem aufrufen muss, das entscheidet, eine Tabelle zu verwenden, weil die Objekte in der Pipeline weniger als fünf Eigenschaften haben (für mehr Eigenschaften verwendet die Shell standardmäßig eine Liste).

Aber ich muss mich nicht auf das Standardverhalten verlassen. Ich kann einfach die Objekte an ein anderes Cmdlet senden, um sie auf andere Weise zu verarbeiten:

@("one","two","three") | StringVersions | Format-List
@("one","two","three") | StringVersions | ConvertTo-HTML | Out-File "strings.html"
@("one","two","three") | StringVersions | Export-CSV "strings.csv"
@("one","two","three") | StringVersions | Select Uppercase,Lowercase -unique

Abbildung 2 zeigt das sich ergebende HTML aus dem zweiten Beispiel in einem Webbrowser. Durch die einfache Repräsentation meiner Ausgabedaten durch Objekte statt als einfachen Text erhalte ich jetzt vollständigen Zugang zu einer Vielzahl von Funktionen, die in die Shell integriert sind: viele verschiedene Formatierungslayouts, HTML-Konvertierung, Exportoptionen, Sortier-, Filter- und Gruppiermöglichkeiten und mehr.

Abbildung 2 Windows PowerShell-Datenausgabe im HTML-Format

Abbildung 2 Windows PowerShell-Datenausgabe im HTML-Format(Zum Vergrößern auf das Bild klicken)

Das alles sind sehr leistungsfähige Funktionen. Ich würde sogar so weit gehen zu behaupten, dass jedes von Ihnen geschriebene Skript ein Objekt als Ausgabe produzieren sollte, damit Sie diese Ausgabe auf so viele unterschiedliche Weisen wie möglich verwenden können, ohne hierzu eine einzige zusätzliche Codezeile schreiben zu müssen.

Don Jones ist Experte im Bereich administrative Windows-Automatisierung und Autor von Büchern wie Windows PowerShell: TFM und VBScript, WMI, and ADSI Unleashed. Sie können ihn über die Foren unter ScriptingAnswers.com erreichen.

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