Windows PowerShellForschrittsbericht

Don Jones

Ich habe vor Kurzem ein ziemlich langes und kompliziertes Windows PowerShell-Skript geschrieben, das während der Ausführung nur sehr langsam reagierte. Es sollte als geplanter Task ausgeführt werden, sodass nur eine geringe sichtbare Ausgabe erzeugt wurde. Bei der ersten großen Testausführung machte ich mir dennoch Sorgen, ob ich nicht

versehentlich eine Endlosschleife oder einen anderen Fehler in das Skript eingebaut hatte.

Angesichts der inaktiven Shell und des nervtötenden, kleinen blinkenden Zeigers fragte ich mich, ob das Skript möglicherweise zerstört war. Offensichtlich habe ich kein Vertrauen in meine eigenen Fähigkeiten. Ich gab in Windeseile Strg+C ein, um das Skript zu unterbrechen. Es ist an der Zeit, Fortschrittsberichterstellung hinzuzufügen.

Anweisungen über Anweisungen

Als Erstes sollte eine Reihe von Statusmeldungen hingefügt werden, mit denen die jeweiligen Aktivitäten des Skripts genau angezeigt werden. In der Shell lässt sich dies mit dem Write-Verbose-Cmdlet relativ einfach bewerkstelligen. Probieren Sie Folgendes in der Shell aus:

Write-Verbose "Test Message"

Sie werden nach dem Versuch festgestellt haben, dass nichts bewirkt wurde. Das liegt daran, dass Write-Verbose Objekte an die spezielle Verbose-Pipeline sendet, die standardmäßig keine Ausgabe anzeigt. Diese Pipeline wird durch eine integrierte Shellvariable, $VerbosePreference, kontrolliert. Der Standardwert für diese Variable ist SilentlyContinue und unterdrückt eine ausführliche Ausgabe. Durch Einstellen auf „Continue“ wird jedoch die Pipeline geöffnet:

$VerbosePreference = "Continue"

Jetzt können Sie dem Skript eine Vielzahl an Write-Verbose-Anweisungen hinzufügen und während der Ausführung eine detaillierte Ansicht erhalten. Der Vorteil an diesem Verfahren besteht darin, dass nach dem Testen und der Fehlerbehebung alle zusätzlichen Anweisungen durch Einstellen von $VerbosePreference am Anfang des Skripts zurück auf SilentlyContinue beendet werden können.

Es ist nicht erforderlich, alle Write-Verbose-Anweisungen manuell zu entfernen. Da sie im Skript verbleiben, kann die Verbose-Pipeline bei Bedarf für jede manuelle Skriptausführung problemlos wieder aktiviert werden.

Wo bleibt der wahre Fortschritt?

Nachdem ich mich vergewissert hatte, dass das Skript nicht in einer Endlosschleife aufgehalten wurde, sondern perfekt funktionierte, habe ich die Verbose-Pipeline deaktiviert und das Skript zur Überprüfung erneut ausgeführt.

Obwohl ich genau weiß, dass das Skript perfekt funktioniert, kann ich den Anblick des blinkenden Cursors allerdings immer noch nicht ertragen. (Anscheinend gibt es auch bei mir Konzentrationsprobleme, und ich fing schon an, mich verzweifelt nach einer sinnvolleren Tätigkeit umzusehen.)

Mir fehlte ganz einfach ein Hinweis auf den generellen Fortschritt des Skripts mit einer ungefähren Angabe des Abschlusses. Im Grunde brauchte ich so etwas wie eine Fortschrittsanzeige.

Glücklicherweise enthält Windows PowerShellTM das Write-Progress-Cmdlet. Dieses Cmdlet bietet zwar keine grafische Fortschrittsanzeige wie in Windows®, aber immerhin eine brauchbare Fortschrittsanzeige (siehe Abbildung 1). Sie gleicht ein wenig der Fortschrittsanzeige beim Kopieren von Dateien, die im textbasierten Teil der Installation in Windows Server® 2003 oder sogar Windows XP verwendet wird.

Abbildung 1 Wie weit ist Ihr Skript fortgeschritten?

Abbildung 1** Wie weit ist Ihr Skript fortgeschritten? **(Klicken Sie zum Vergrößern auf das Bild)

Die Verwendung von Write-Progress muss etwas näher erläutert werden. Sie lässt sich am besten anhand eines Beispiels veranschaulichen. Sehen Sie sich das folgende Skript an:

for ($a=1; $a -lt 100; $a++) {
  Write-Progress -Activity "Working..." `
   -PercentComplete $a -CurrentOperation
   "$a% complete" `
   -Status "Please wait."
  Start-Sleep 1
}

Es verwendet Write-Progress, um eine Fortschrittsanzeige anzuzeigen. Es wurde Start-Sleep verwendet, um das Skript nach jeder Schleife eine Sekunde lang anzuhalten. Auf diese Weise ist die Ausführung langsam genug, um den Fortschritt zu beobachten. Ohne die Pause zählt die Schleife so schnell von 0 bis 100, dass die Fortschrittsanzeige nur kurz auf dem Bildschirm aufleuchtet.

Wie Sie sehen, wird die ausgeführte Aktivität am oberen Rand der Fortschrittsanzeige aufgeführt. Der Status wird direkt darunter angezeigt, der aktuelle Vorgang am unteren Rand. Die Shell kann nicht mehrere Fortschrittsanzeigen gleichzeitig unterstützen. Bei jeder Verwendung von Write-Progress wird entweder eine neue Fortschrittsanzeige erstellt, falls zurzeit keine angezeigt wird, bzw. die aktuell angezeigte Fortschrittsanzeige wird aktualisiert.

In diesem Beispiel wurde die Fortschrittsanzeige allerdings nicht angewiesen, nach getaner Arbeit zu verschwinden. Hierfür müssen Sie am Ende des Skripts lediglich Folgendes hinzufügen:

Write-Progress -Activity "Working..." `
 -Completed -Status "All done."

In der Regel verschwindet die Fortschrittsanzeige direkt nach Abschluss des Skripts von allein. Wenn Ihr Skript jedoch weiterhin beschäftigt ist, soll die Fortschrittsanzeige anschließend ausgeblendet werden. Mit dem -Completed-Parameter können Sie die Fortschrittsanzeige aus der Anzeige entfernen.

Sekundenanzeige

Eine andere häufige Verwendung von Write-Progress besteht darin, eine Anzeige der verbleibenden Sekunden anstatt einer Fortschrittsanzeige zu erstellen. Hier ist ein Beispiel:

for ($a=100; $a -gt 1; $a--) {
  Write-Progress -Activity "Working..." `
   -SecondsRemaining $a -CurrentOperation
   "$a% complete" `
   -Status "Please wait."
  Start-Sleep 1
}

Es wurde einfach die Schleifenzählung in 100 bis 1 geändert und der SecondsRemaining-Parameter von Write-Progress anstatt PercentComplete verwendet. Das Ergebnis ist in Abbildung 2 dargestellt. Wie Sie sehen, wurde die Fortschrittszeitanzeige durch eine Countdownuhr ersetzt. Die Shell konvertiert automatisch die Gesamtanzahl der verbleibenden Sekunden in Stunden, Minuten und Sekunden und stellt so benutzerfreundlichere Informationen zur Verfügung. Die hier gezeigte prozentuale Fortschrittsanzeige zählt von 100 abwärts, da dies der zur Verfügung gestellte CurrentOperation-Parameter ist. Es gibt keine Berechnung der prozentualen Fertigstellung. Windows PowerShell zeigt einfach den aktuellen Wert von $a an, gefolgt von der Zeichenfolge „% complete“.

Abbildung 2 Wie lange dauert die Fertigstellung des Skripts?

Abbildung 2** Wie lange dauert die Fertigstellung des Skripts? **(Klicken Sie zum Vergrößern auf das Bild)

Skripts, die kommunizieren

Ich bin ein großer Anhänger des Schreibens von Skripts, die mich über ihre Aktivitäten informieren. Dies kann in Form einer ausführlichen Ausgabe oder lediglich über eine einfache Fortschrittsanzeige erfolgen und hilft mir hier und heute bei der Konzentration, zugleich aber auch anderen Benutzern, die das Skript in Zukunft ausführen müssen. Letzten Endes kann die Anzeige von Status- und Fortschrittsinformationen, in welcher Form auch immer, nur von Vorteil sein.

Cmdlet des Monats: Tee-Object

In diesem Monat möchte ich eines meiner Lieblings-Cmdlets zur Fehlerbehebung vorstellen. Sehen Sie sich z. B. Folgendes an:

Get-WMIObject Win32_Service | Where { $_.State -ne "Running"
-and $_.StartMode -eq "Automatic" } | ForEach-Object { $_.Start() }

Auf den ersten Blick müssten dadurch alle Dienste gestartet werden, für die ein automatischer Start festgelegt wurde, die aber aus unterschiedlichen Gründen noch nicht gestartet wurden. Dies funktioniert aber nicht wirklich. Außerdem kann sich das Ermitteln der Gründe als sehr kompliziert erweisen, weil Sie die Mitte der Pipeline nicht einsehen können. Das heißt, Sie können die Mitte der Pipeline nur einsehen, wenn Sie Tee-Object verwenden.

Tee-Object leitet Objekte an eine Datei um (oder in eine Variable) und übergibt sie entlang der Pipeline. Beispiel:

Get-WMIObject Win32_Service | Tee-Object AllServices.csv | Where 
{ $_.State -ne "Running" -and $_.StartMode -eq "Automatic" } | 
Tee-Object FilteredServices.csv | ForEach-Object { $_.Start() }

Anhand dieser Änderung lässt sich erkennen, was nach jedem Pipelinebefehl geschieht. Es ist sofort ersichtlich, dass die FilteredServices.csv-Datei keinen Inhalt hat. Kein Wunder, dass dieses Skript nicht funktioniert hat! Weitere Nachforschungen weisen auf die Ursache des Problems hin: StartMode ist „Auto“ und nicht „Automatic“, und mithilfe von Tee-Object können Sie genau feststellen, an welcher Stelle das Problem auftrat.

Don Jones schreibt redaktionelle Beiträge für das TechNet Magazin und ist Mitautor von Windows PowerShell: TFM (SAPIEN Press, 2007). Er hält Schulungen zu Windows PowerShell ab (siehe www.ScriptingTraining.com). Sie erreichen ihn über die Website ScriptingAnswers.com.

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