PowerShell Debugging
Menschen neigen zu Fehlern und selbst in den kürzesten Skripten können sich Fehler verbergen, die nicht sofort auffallen
Strenge Definition mit Set-PSDebug und Set-Strictmode
Ehe ich in die Tiefen einer Funktionen und Tricks gehe, sollten Sie sich wirklich überlegen eine "Plicht zur Definition" für Variablen vorzugeben. Die meisten Fehler passieren nämlich durch Tippfehler, dass Sie eine Variable belegen und anderswo mit einem anderen Namen benutzen. Die folgenden Zeilen sorgen dafür, dass nicht initialisierte Variablen zu einem Fehler führen.
# In VBScript ist das ein # option explicit set-psdebug -strict
Besser so einen Fehler finden als mir zufälligen oder gar falschen Inhalten zu arbeiten. Wer schon die Variablen vorab initialisiert, sollte Sie auch gleich typisieren, d.h. einen String auch mit "[string]" voran definieren, damit Powershell auch weiß, ob ein "+" (Plus-Zeichen) nun zwei Variablen verketten oder die Inhalte addieren soll.
- Set-StrictMode
https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/set-strictmode - Troubleshoot by Using Set-PSDebug
http://blogs.technet.com/b/heyscriptingguy/archive/2014/12/02/troubleshoot-by-using-set-psdebug.aspx - Enforce Better Script Practices by Using
Set-StrictMode
https://blogs.technet.microsoft.com/heyscriptingguy/2014/12/03/enforce-better-script-practices-by-using-set-strictmode/ - Set-StrictMode and Set-PSDebug -strict
https://www.packtpub.com/mapt/book/networking_and_servers/9781782173571/5/ch05lvl1sec43/set-strictmode-and-set-psdebug--strict
Fun mit set-psdebug
Allerdings hat diese Zeile auch einen Bug offenbart, den ich zufällig bemerkt habe z.B. wenn ich folgenden Zeilen nutze:
set-psdebug -strict mkdir ".\logs" -force | out-null
Legen Sie diese Zeilen als PS1-Datei ab und starten Sie diese dann aus einer CMD-Box mit:
PowerShell.exe -noninteractive -file "c:\temp\test.ps1"
Es könnte sein, dass Sie dann folgende Fehlermeldung sehen
The variable '$_' cannot be retrieved because it has not been set. At line:2 char:38 + $steppablePipeline.Process($_ <<<< ) + CategoryInfo : InvalidOperation: (_:Token) [], ParentContainsErrorRecordException + FullyQualifiedErrorId : VariableIsUndefined
Das Problem tritt nicht auf, wenn Sie die beiden Zeilen einfach drehen. Ich weiß nicht, wann "$_" tatsächlich von der PowerShell instanziert wird, aber anscheinend nicht früh genug für PSDEBUG
mkdir ".\logs" -force | out-null set-psdebug -strict
- PowerShell mkdir alias +
Set-StrictMode -Version 2.
Strange bug
http://stackoverflow.com/questions/5021720/PowerShell-mkdir-alias-set-strictmode-version-2-strange-bug-why - http://pastebin.com/pApdN2t9
- Strict Mode: V1 Debug Tool
V1 / V2 Best Practice
http://blogs.msdn.com/b/PowerShell/archive/2008/05/21/strict-mode.aspx
Ausgaben
Zudem kann man an verschiedenen Stellen mit den drei "Write"- befehlen weitere Infos ausgeben, die einfach per Default abschaltbar sind.
Hinweis
Jeffrey Snover rät davon ab, mit "Write-Host" Debug-Ausgaben
zu schreiben, da sie "nur" auf der Konsole erscheinen und
daher nicht weiter verarbeitet werden können. Das
widerspricht dem Ziel Code wieder zu verwenden. Write-Host
ist aber durchaus in Ordnung für z.B. die Gestaltung
einfacher Oberflächen u.a.
Siehe auch
http://www.jsnover.com/blog/2013/12/07/write-host-considered-harmful/
Ich habe es schon mehrfach erlebt, dass "Write-Host" Ausgaben nicht mit Start-Transcript erfasst werden, was die Fehlersuche ebenfalls nicht gerade macht.
write-host "Hallo ich starte" write-warning "Ich bin eine Warnung" write-error "Das ist ein Error" write-debug "Das ist eine Debugmeldung 1" $DebugPreference = "Continue" write-debug "Das ist eine Debugmeldung 2" $DebugPreference = "SilentlyContinue" write-debug "Das ist eine Debugmeldung 3" write-host "Hallo, ich bin fertig"
So sieht die Ausgabe dann aus.
Die "write-error"-Meldung beendet das Skript aber nicht.
Wenn Sie bei der Ausgabe eine Zeile immer wieder überschreiben lassen wollten (z. B. als Statusanzeige), dann helfen ihnen Sonderzeichen und die Option "-nonewline"
while ($true) { write-host "`r " (Get-Date -Format HHMMss) -NoNewline Start-Sleep 1 }
Weitere Sonderzeichen sind:
`0 Null `a Alert `b Backspace `f Form feed `n New line `r Carriage return `t Horizontal tab `v Vertical tab
- about_Special_Character
http://technet.microsoft.com/en-us/library/dd347558.aspx - Write-Host Considered Harmful
http://www.jsnover.com/blog/2013/12/07/write-host-considered-harmful/ - Write-Host Debugging
PowerShell und Breakpoints
Ich erinnere mich noch gut an das "Debugging" unter DOS. In einem Batch hat man einfach an der gewünschten Stelle ein "COMMAND:EXE" eingefügt und damit das Skript unterbrochen und konnte in der Shell bestimmte Dinge nachschauen, z.B. Variablen. Mit einem EXIT wurde dieser Child-Prozess verlassen und das eigentliche Skript konnte weiter arbeiten. PowerShell bietet diese Funktion auch aber auf einem viel höheren Niveau.
Hier sind es "Breakpoints"
# Hilfe zum PS Debugger anzeigen get-help about_debuggers # Verfügbaren Commandlets anzeigen get-help *psbreak* Name Category Synopsis ---- -------- -------- Set-PSBreakpoint Cmdlet Legt einen Haltepunkt für eine Z... Get-PSBreakpoint Cmdlet Ruft die Haltepunkte ab, die in ... Remove-PSBreakpoint Cmdlet Löscht Haltepunkte aus der aktue... Enable-PSBreakpoint Cmdlet Aktiviert die Haltepunkte in der... Disable-PSBreakpoint Cmdlet Deaktiviert die Haltepunkte in d... # PSDEbug aktivieren set-psdebug
Allerdings dürften die meisten PowerShell und Exchange Einsteiger mit diesen Funktionen anfangs überfordert sein. Vielleicht ist die ISE (Integrated Script Environment) mit dem dort eingebauten Debugger oder ein Programm wie PowerGUI für den Anfang sogar besser geeignet, um die ersten Gehversuche mit PowerShell bei der Fehlersuche zu unterstützen.
- Introducing the Windows
PowerShell ISE
http://technet.microsoft.com/en-us/library/dd315244.aspx
PowerShell und "Debug"-Schnittstelle
Windows hat intern eine "Debug-Schnittstelle", in welche Programme ihre Ausgaben einstellen können und andere Programme können sich für den Empfang dieser Nachrichten registrieren. Wenn kein Programm lauscht, dann verwirft Windows die Meldungen und es kostet kaum Ressourcen. Ein gutes Programm zum Anzeigen ist z.B. DebugView von Sysinternals. Und es ist aus PowerShell denkbar einfach, eine Meldung an diese Schnittstelle zu überstellen.
[System.Diagnostics.Debug]::WriteLine("Nachricht","Katgorie")
Diese Schnittstelle bietet sich also für viele Detailausgaben, die im Fehlerfall oder zur Analyse herangezogen werden können aber ansonsten nicht besonders beachtet werden müssen. Das ist insbesondere interessant, wenn mehrere Prozesse im Hintergrund laufen.
Eine weitere Option ist die Aktivierung eines Trace mit.
Set-PSDebug -trace 0
Spielen Sie einfach mal damit herum, welches Hilfsmitteln ihnen besser hilft. Ich habe mir mittlerweile eine eigene Funktion für das Debugging gebaut, der ich die Meldung und einen Schweregrad übergebe, die dann optional einen Bildschirmausgabe erzeugt, eine Eventlogeintrag schreibt oder eine Mail versendet.
Zuletzt können Sie natürlich auch alles protokollieren lassen, was auf der Konsole passiert.
Start-Transcript -Path e2k7rUS-$starttime.log -Append Stop-Transcript
Allerdings werden das dann eventuell schon etwas umfangreichere Textdateien. Aber wenn ein Skript viel Ausgaben produziert und der Bildschirmbuffer überläuft, ist es oft die einfachste Möglichkeit, etwas nach zu vollziehen.
PowerShell Debugger
Es gibt von Microsoft keine richtige "EntwicklungsUmgebung" und schon gar keinen "Debugger" für PowerShell. Allerdings wurde in der Version 2. eine Debug-Funktion eingebaut, mit der Sie ein Skript bei Fehlern oder über Vorgaben unterbrechen können. Die sich dann öffnende Box ist eine "Untershell", so dass Sie alle Variablen zum aktuellen Stand einsehen und auch verändern können. Mit einem EXIT geht es dann einfach weiter und mit einem "{break}" können Sie das Skript abbrechen. Das ist natürlich kein Vergleich zu den Debug-Funktionen von PowerGUI und anderen Programmen. Weitere Details finden Sie auf folgendem TechNet Artikel.
- The Windows PowerShell Debugger
http://www.microsoft.com/technet/scriptcenter/topics/winpsh/debug.mspx
Testen statt Debuggen
Anstatt immer "hinterher" nach Fehlern zu suchen gibt es interessante Frameworks, mit denen Sie beschreiben, was ein Skript, eine Funktion oder ein Modul leisten soll. Damit lassen sich dann Tests automatisieren. Zugegeben, bislang habe ich damit noch nicht gearbeitet:
- Testing PowerShell with Pester
https://blogs.msdn.microsoft.com/mvpawardprogram/2017/05/30/testing-powershell-with-pester/ - https://GitHub.com/Pester/Pester/wiki
- https://GitHub.com/pester/Pester
- Describe Your PowerShell Variables
https://blogs.technet.microsoft.com/heyscriptingguy/2015/07/27/describe-your-powershell-variables/
Weitere Links
- Write-Host Debugging
- Write-Host Considered Harmful
http://www.jsnover.com/blog/2013/12/07/write-host-considered-harmful/ - The Windows PowerShell Debugger
http://www.microsoft.com/technet/scriptcenter/topics/winpsh/debug.mspx - Introducing the Windows
PowerShell ISE
http://technet.microsoft.com/en-us/library/dd315244.aspx