Debugging mit Haltepunkten
Als Debugging bezeichnet man den Prozess der Fehlersuche in Programmen. Ein Debugger ist dabei das Schweizer Taschenmesser, dass Dir bei der Fehlersuche hilft - und Powershell liefert natürlich bereits einen in die Sprache integrierten Debugger mit. Den Umgang mit dem Debugger zu lernen, ist die vielleicht wichtigste Fählgkeit, die man lernen kann, um Powershell-Code besser zu verstehen und lange Skripte in den Griff zu bekommen.
Das Hauptfeature eines Debuggers sind sogenannte Haltepunkte oder Breakpoints. Sie erlauben es, den Code während der Ausführung kontrolliert zu bestimmten Ereignissen anzuhalten, um den Zustand der Laufzeitumgebung zu überprüfen – beispielsweise den Wert, der in einer Variablen gerade gespeichert ist. Haltepunkte setzt man mit dem Cmdlet Set-PSBreakPoint. Wenn ein Haltepunkt gesetzt ist, merkt sich Powershell im Arbeitsspeicher, in welcher Zeile die Skriptausführung angehalten werden soll. Da das Skript selbst nicht verändert wird, kann man Haltepunkte nur für gespeicherte Skripte setzen.
Set-PSBreakpoint -Script c:\Skripte\DebugDemo.ps1 -Line 10
Um einen Haltepunkt zu entfernen, brauchen Sie nur den Powershell-Prozess zu beenden. Alternativ können Sie mit Disable-PSBreakpoint einen Haltepunkt deaktiviert, oder ihn mit Remove-PSBreakpoint entfernen.
Get-PSBreakpoint | Disable-PSBreakpoint
Get-PSBreakpoint | Enable-PSBreakpoint
Get-PSBreakpoint | Remove-PSBreakpoint
Das erste Beispiel in Zeile 1deaktiviert alle Haltepunkte, ohne sie zu löschen. Das zweite Beispiel reaktiviert die Haltepunkte wieder, und das letzte Beispiel entfernt alle Haltepunkte.
Sie können Haltepunkte auch direkt im Editor konfigurieren. In der ISE setzen Sie hierfür den Cursor in die Zeile, in der der Haltepunkt gesetzt werden soll, wechseln in das Menü "Debuggen" und wählen "Haltepunkt umschalten". Alternativ und viel schneller geht es, wenn Sie die F9-Taste drücken. Wenn Sie den Haltepunkt nicht setzen können, prüfen Sie, ob die Datei schon gespeichert wurde. Geben Sie anschließend Get-PSBreakPoint in der Konsole ein, werden Sie feststellen, dass die ISE nichts anderes gemacht hat, als für Sie das Cmdlet Set-PSBreakPoint im Hintergrund aufzurufen.

In der Abbildung der ISE sehen Sie einen braun markierten Haltepunkt. Er ist innerhalb einer Funktion gesetzt. Die Ausführung des Skriptes mit F5 führt noch nicht zum Aktivieren des Haltepunktes. Erst wenn Sie die Funktion aufrufen, wird der Haltepunkt ausgelöst und das Skript pausiert. In der Konsole wird der Text "Treffer Zeilenhaltepunkt bei <Skript>:Codezeile" angezeigt, und der Prompt zeigt ein führendes [DBG]: an. Powershell hat jetzt den kompletten Code bis Zeile 13 ausgeführt. Sie können in der Debugkonsole den Zustand der Shell jetzt prüfen, indem Sie sich Variablen ausgeben lassen. Sie können auch Skriptcode ausführen.
[DBG]: PS C:\Users\Holger>> $FolderData
> Count : 65819
> Average :
> Sum : 15270776814
> Maximum : 2524096521
> Minimum : 0 Property : Length
Im Visual Studio Code gibt es einen eigenen Button in der Seitenleiste, der einen Käfer auf einem Play-Button darstellt.

Wählen Sie den Button (1), um die Debug-Leiste zu öffnen. Die Haltepunkte können Sie in der aktiven Zeile wie in der ISE mit der F9 Taste aktivieren oder deaktivieren, oder alternativ mit der Maus, wenn Sie links neben die Zeilennummern klicken (3). Im Debugmenü werden die gesetzen Haltepunkte aufgelistet (4), aber irritierenderweise sind sie noch gar nicht in der Powershell gesetzt. Wenn Sie Ihr Skript starten, wird der Debugger nicht aktiviert. Wenn Sie Get-PSBreakpoint eingeben, werden keine Haltepunkte aufgelistet. Erst wenn Sie den Button "Ausführen und Debuggen" (2) klicken, werden die Haltepunkt tatsächlich auch in der Powershell gesetzt. Das Gleiche gilt im übrigen auch für das Deaktivieren. Ändern Sie Haltepunkt, müssen Sie in VSCode immer erste einmal mit "Ausführen und debuggen" die Haltepunkte aktualisieren. Denken Sie außerdem daran, dass Haltepunkte in Funktionen nicht beim Ausführen des Skriptes, sondern immer erst beim Aufrufen der Funktion greifen.
Nachdem Sie Haltepunkte gesetzt haben, können Sie den Code ausführen. Wenn Powershell auf den Haltepunkt trifft, stoppt die Ausführung.
Sie können das Skript ab jetzt zeilenweise durchgehen, indem Sie in der Konsole "s" für step eingeben, oder (in der ISE) die Funktionstaste F11 betätigen. So können Sie das Verhalten Ihres Skriptes Zeile für Zeile prüfen. Dabei werden Sie feststellen, dass auch Schleifen schrittweise verarbeitet werden:

Die Ockerfarbene Markierung zeigt die Zeile, in der Powershell gerade wartet, hier also in der runden Klammer des Foreach-Statements.
Der Debugger springt sogar in Unterfunktionen, die gar nicht Teil des Skriptes sind, wie folgende Beispiel zeigt:

In Zeile 3 des Skripts ist ein Haltepunkt gesetzt. Sobald Sie im Debug-Modus mit "s" die nächste Code-zeile ausführen, wird ein neues Skriptfenster in der ISE geöffnet. Sie debuggen jetzt nämlich die Funk-tion Test-NetConnection, da dieses Powershell-eigene Kommando von Microsoft auch als Funktion im-plementiert wurde.

Mit den zwei zusätzlichen Debugger-Befehlen StepO(v)er und Step(O)ut können Sie Ihr Skript dort weiter fortführen, wo es Sie interessiert. Wenn Sie im Beispielskript in Abbildung statt "s" "v" eingeben oder in der ISE F10 drücken, überspringt Powershell die Funktion Test-Netconnection und bleibt in der nächsten Zeile Ihres eigenen Skriptes stehen. Ist das Kind bereits in den Brunnen gefallen wie in der Abbildung, können Sie mit "o" bzw. Shift+F11 die Unterfunktion wieder verlassen.
In einer Schleife oder in der Pipeline können Sie mit StepOver auch die Verarbeitung von Klammern überspringen. Wenn Ihr Haltepunkt vor einer Schleife liegt, können Sie so also die gesamte Schleife übergehen und direkt nach der Schleife weiter debuggen. Achten Sie aber darauf, dass das bei einem Haltepunkt innerhalb der Schleife nicht funktioniert, da der Haltepunkt mit jedem Schleifendurchlauf neu ausgelöst wird.
Wenn Sie Ihren Fehler gefunden haben, können Sie die komplette Debug-Verarbeitung des Skripts mit "q" einfach beenden. Der Debugger bricht dann ab, und das Skript wird beendet. Alternativ können Sie auch "c" für Continue wählen, um Ihr Skript weiter auszuführen. Das ist vor allem dann sinnvoll, wenn Sie weitere Haltepunkte aktiviert haben oder Sie das (hoffentlich jetzt korrekte) Ergebnis Ihres Skriptes sehen wollen.
Um sich alle Kommandozeilenkürzel anzeigen zu lassen, können Sie im Debugger auch einfach "?" eingeben. Powershell gibt Ihnen dann eine kurze Hilfe mit allen verfügbaren Debug-Kommandos aus:
[DBG]: PS C:\Users\Holger>> ?
> s, stepInto Einzelschritt (in Funktionen, Skripts usw.)
> v, stepOver Prozedurschritt zur nächsten Anweisung
> o, stepOut Rücksprung aus der aktuellen Funktion, dem Skript usw.
c, continue : Vorgang fortsetzen
q, quit : Vorgang und Debugger beenden
d, detach : Setzen Sie den Vorgang fort, und trennen Sie den Debugger.
k, Get-PSCallStack - Aufrufliste anzeigen
Im VSCode ist der Debugger deutlich funktionaler.

Ist der Debugger gestartet, kann man mit einer Leiste am oberen Bildschirmrand das Skript fortführen (2). Der Linke Button mit dem Play-Symbol führt das Skript weiter aus, bis der nächste Haltepunkt erreicht ist. Der Prozedurschritt (step over) springt in die nächste Zeile des Codes, überspringt aber Unterprozeduren, verlässt das Skript also nicht, im Gegensatz zum nächsten Button, dem Einzelschritt. Ist man in einer Unterprozedur gelandet, kann man Sie mit dem nächsten Button (Punkt mit Pfeil nach oben) wieder verlassen. Der gründe, runde Pfeil startet die Prozedur von vorne, der Stop-Button beendet die Ausführung sofort. Alle Optionen lassen sich auch bequem von der der Tastatur aus Steuern. Die zugehören Tastenkürzel werden angezeigt, wenn man mit der Maus über die Buttons fährt.
Auch im VSCode wird die aktuelle Zeile mit einem farbigen Balkem im Code markiert (1). Sehr hilfreich ist die Anzeige der Debugging-Leiste links, denn hier kann man sich den Inhalt aller Variablen anzeigen lassen (3). Wenn man eine oder mehrere bestimmte Variablen beobachten möchte, kann man das mit dem Wachtlist (Überwachen) machen (4). Hier kann man einzelne Variablen über den +-Button eintragen, oder Sie aus der Liste unter (3) hinzufügen, indem man aus dem Kontextmenü der Variablen "Zur Überwachung hinzufügen" auswählt.
Der Debugger von Powershell kann aber noch deutlich mehr. So kann man z.B. auch Haltepunkte auf Variablen setzen, die das Skript immer dann anhalten, wenn eine Variable geschrieben oder gelesen wird.
Denjenigen, die echte Powershell Debugging-Ninjas werden wollen empfehle ich den Talk von Kirk Munro auf der Powershell + Devops Global Summit 2018:
[DBG]: PS C:\Users\Holger>> ?
> s, stepInto Einzelschritt (in Funktionen, Skripts usw.)
> v, stepOver Prozedurschritt zur nächsten Anweisung
> o, stepOut Rücksprung aus der aktuellen Funktion, dem Skript usw.
c, continue : Vorgang fortsetzen
q, quit : Vorgang und Debugger beenden
d, detach :Setzen Sie den Vorgang fort, und trennen Sie den Debugger.
k, Get-PSCallStack - Aufrufliste anzeigen