Hibakeresés

Akármennyire sok lehetőségünk van a hibák jelzésére (write-warning, write-error és trap), kezelésére mégsem minden esetben elég ez. Főleg akkor, ha a szkriptünk a maga módján jól működik, csak éppen mi rossz programot írtunk. Ilyenkor van szükség a „dibággolásra”, „bogártalanításra”, azaz a hibakeresésre. Erre is lehetőséget biztosít a PowerShell, bár valószínű egy bizonyos bonyolultság felett már inkább használunk erre a célra valamilyen grafikus szkriptszerkesztőt, mint például a PowerShell Integrated Scripting Environmentjét vagy a PowerGUI Script Editorát.

Státusjelzés (write-verbose, write-warning, write-debug)

Az egyik „bogártalanítási” módszer, hogy jól teletűzdeljük a programunkat kiíratási parancsokkal, amelyek segítségével a változók aktuális állapotát írhatjuk ki, illetve jelezhetjük, hogy éppen milyen ágon fut a programon. Erre használhatjuk akár a write‑host cmdletet, csak az a baj ezzel, hogy a végleges, kijavított, tesztelt programból elég nehéz kiszedegetni ezeket. Ezért találtak ki másfajta kiíratási lehetőségeket is: a write-verbose, write-warning  és a write-debug  cmdleteket.

Nézzünk erre példát. Van egy faktoriálist számoló szkriptem, amit jól teletűzdelek write-verbose helyzetjelentésekkel:

Write-Verbose "Eleje"

$a = 10; $result = 1

Write-Verbose "Közepe $a"

for($i=1; $i -le $a; $i++)

     {

          $result = $result * $i

          Write-Verbose "`$i = $i, `$result = $result"

     }

Write-Verbose "Vége"

$result

Nézzük, mi történik, ha futtatom:

[7] PS C:\powershell2\scripts> .\verbose.ps1

3628800

Nem sok mindent látunk ezen, mintha ott sem lennének a write-verbose parancsok. Merthogy a write-verbose hatását egy globális változóval, a $VerbosePreference -szel lehet ki- és bekapcsolni:

[9] PS C:\powershell2\scripts> $verbosepreference = "continue"

[10] PS C:\powershell2\scripts> .\verbose.ps1

VERBOSE: Eleje

VERBOSE: Közepe 10

VERBOSE: $i = 1, $result = 1

VERBOSE: $i = 2, $result = 2

VERBOSE: $i = 3, $result = 6

VERBOSE: $i = 4, $result = 24

VERBOSE: $i = 5, $result = 120

VERBOSE: $i = 6, $result = 720

VERBOSE: $i = 7, $result = 5040

VERBOSE: $i = 8, $result = 40320

VERBOSE: $i = 9, $result = 362880

VERBOSE: $i = 10, $result = 3628800

VERBOSE: Vége

3628800

Nyomtatásban esetleg nem látszik, de a VERBOSE: kimenetek szép sárgák, hasonlóan a „Warning” jelzésekhez. Ennek további értékei lehetnek – hasonlóan az $ErrorActionPreference változóhoz – SilentlyContinue (alapérték), Inquire, Stop, valamint PowerShell 3.0-tól kezdődően van még az Ignore  lehetőség is.

Ugyanígy működik a write-debug  cmdlet is, csak az ő hatását a $DebugPreference  változó szabályozza.

Azaz van két, egymástól függetlenül használható kiírató cmdletünk, amelyekkel a szkriptjeink futásának státusát írathatjuk ki a fejlesztés során, majd ha készen vagyunk és meggyőződtünk, hogy minden jól működik, akkor anélkül, hogy a szkriptből ki kellene szedegetni ezeket a státusjelzéseket, egy globális változó beállításával ezek hatását ki tudjuk kapcsolni.

Lépésenkénti végrehajtás és szigorú változókezelés (set-psdebug)

A másik hibaűző módszer, ha lépésenként hajtatjuk végre a szkriptünket és így jobban mg tudjuk figyelni, hogy merre jár. Azaz nem kell minden sor után egy write-debug sort beiktatni. Erre is lehetőséget biztosít a PowerShell a Set-PSDebug  cmdlettel. Nézzük meg a szintaxisát:

[22] PS C:\powershell2\scripts> (get-help set-psdebug).syntax

 

Set-PSDebug [-Off] [<CommonParameters>]

Set-PSDebug [-Step] [-Strict] [-Trace <int>] [<CommonParameters>]

A második változatban kapcsolhatjuk be a hibakeresési üzemmódot. A –trace paraméter lehetséges értékei:

0: kikapcsoljuk a nyomkövetést

1: csak a végrehajtás során az aktuális sor számát jelzi

2: a sorok száma mellett a változók értékadását is jelzi

Az előző szkriptet fogom használni, kicsit átalakítva:

$a = 3; $result = 1

for($i=1; $i -le $a; $i++)

     {

          $result = $result * $i

     }

$result

És akkor nézzük a –Trace 1 opcióval a futását:

PS C:\> Set-PSDebug -Trace 1

DEBUG:    2+         $foundSuggestion = <<<<  $false

DEBUG:    4+         if <<<< ($lastError -and

DEBUG:   15+         $foundSuggestion <<<<

Érdekes módon, saját magát is „megnyomkövette”!

PS C:\> .\_munka\powershell2\scripts\psdebug.ps1

DEBUG:    1+  <<<< .\_munka\powershell2\scripts\psdebug.ps1

DEBUG:    1+ $a = <<<<  3; $result = 1

DEBUG:    1+ $a = 3; $result = <<<<  1

DEBUG:    2+ for <<<< ($i=1; $i -le $a; $i++)

DEBUG:    4+   $result = <<<<  $result * $i

DEBUG:    4+   $result = <<<<  $result * $i

DEBUG:    4+   $result = <<<<  $result * $i

DEBUG:    6+ $result <<<<

6

DEBUG:    2+         $foundSuggestion = <<<<  $false

DEBUG:    4+         if <<<< ($lastError -and

DEBUG:   15+         $foundSuggestion <<<<

Láthatjuk, hogy szépen kiírja az éppen végrehajtott sorok számát, meg az ottani parancssor tartalmát is. Nézzük a –Trace 2 opció hatását:

PS C:\> Set-PSDebug -Trace 2

DEBUG:    1+  <<<< Set-PSDebug -Trace 2

DEBUG:    2+         $foundSuggestion = <<<<  $false

DEBUG:     ! SET $foundSuggestion = 'False'.

DEBUG:    4+         if <<<< ($lastError -and

DEBUG:   15+         $foundSuggestion <<<<

PS C:\> .\_munka\powershell2\scripts\psdebug.ps1

DEBUG:    1+  <<<< .\_munka\powershell2\scripts\psdebug.ps1

DEBUG:     ! CALL function 'psdebug.ps1'  (defined in file

'C:\_munka\powershell2\scripts\psdebug.ps1')

DEBUG:    1+ $a = <<<<  3; $result = 1

DEBUG:     ! SET $a = '3'.

DEBUG:    1+ $a = 3; $result = <<<<  1

DEBUG:     ! SET $result = '1'.

DEBUG:    2+ for <<<< ($i=1; $i -le $a; $i++)

DEBUG:     ! SET $i = '1'.

DEBUG:    4+   $result = <<<<  $result * $i

DEBUG:     ! SET $result = '1'.

DEBUG:     ! SET $i = '2'.

DEBUG:    4+   $result = <<<<  $result * $i

DEBUG:     ! SET $result = '2'.

DEBUG:     ! SET $i = '3'.

DEBUG:    4+   $result = <<<<  $result * $i

DEBUG:     ! SET $result = '6'.

DEBUG:     ! SET $i = '4'.

DEBUG:    6+ $result <<<<

6

DEBUG:    2+         $foundSuggestion = <<<<  $false

DEBUG:     ! SET $foundSuggestion = 'False'.

DEBUG:    4+         if <<<< ($lastError -and

DEBUG:   15+         $foundSuggestion <<<<

Láthatjuk, hogy az előzőek mellett még a szkripthívást is látjuk, és minden egyes változó értékadását is az új értékekkel együtt. Ha még a –step paramétert is használjuk, akkor minden sor végrehajtására külön rákérdez:

[37] PS C:\powershell2\scripts> Set-PSDebug -Trace 1 -step

[38] PS C:\powershell2\scripts> .\psdebug.ps1

 

Continue with this operation?

   1+ .\psdebug.ps1

[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help

(default is "Y"):y

DEBUG:    1+ .\psdebug.ps1

 

Continue with this operation?

   1+ $a = 3; $result = 1

[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help

(default is "Y"):y

DEBUG:    1+ $a = 3; $result = 1

 

Continue with this operation?

   1+ $a = 3; $result = 1

[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help

(default is "Y"):s

Ekkor választhatunk, hogy tovább lépünk egy sorral (Yes), folytatjuk a futtatást végig (Yes to All), megszakítjuk a futtatást (No és No to All). A Suspend „némán” állítja meg a futtatást, nem lesz hibajelzés a rendkívüli programmegszakításról.

A –strict kapcsoló használata után csak olyan változókra hivatkozhatunk, amelynek korábban adtunk kezdőértéket:

[41] PS C:\powershell2\scripts> Set-PSDebug -strict

[42] PS C:\powershell2\scripts> $c=1

[43] PS C:\powershell2\scripts> $c

1

[44] PS C:\powershell2\scripts> $d

The variable '$d' cannot be retrieved because it has not been set.

At line:1 char:3

+ $d <<<<

    + CategoryInfo          : InvalidOperation: (d:Token) [], RuntimeExceptio

   n

    + FullyQualifiedErrorId : VariableIsUndefined

A fenti példában a $c értékadás után lekérdezhető volt, szemben a $d-vel, amire hibajelzést kaptunk, hiszen nem adtam neki kezdőértéket.

Ha már nincs szükségünk a Set-PSDebug-gal beállított hibafelderítő üzemmódokra, akkor az –Off kapcsolóval kapcsolhatjuk ki ezekez:

[45] PS C:\powershell2\scripts> Set-PSDebug -off

[46] PS C:\powershell2\scripts> $d

[47] PS C:\powershell2\scripts>

Itt látható, hogy már nem okozott hibajelzést az értékadás nélküli $d-re való hivatkozás.

A Set-PSDebug egy globális hatású cmdlet, azaz beállítása után minden futtatási környezet (scope) azonos módon „szigorú” lesz. A PowerShell 2.0-ban egy új cmdletünk is van, a Set-StrictMode . Ez már csak az adott futtatási környezetre és alkörnyezetekre hat, így sokkal könnyebb az éppen hibafelderítés alatt álló részekre beállítani a szigorúságot. A –Version 1 paraméterrel hasonlóan működik, mint a korábban látott szigorú üzemmód. De a –Version 2-vel még szigorúbb lesz:

PS C:\Users\tibi> Set-StrictMode -Version 2

PS C:\Users\tibi> $a="hkh"

PS C:\Users\tibi> $a.yyy

Property 'yyy' cannot be found on this object. Make sure that it exists.

At line:1 char:4

+ $a. <<<< yyy

    + CategoryInfo          : InvalidOperation: (.:OperatorToken) [], Runtime

   Exception

    + FullyQualifiedErrorId : PropertyNotFoundStrict

        Az első sorban beállítottam ezt a fajta szigorúságot a PowerShell 2.0 kompatibilis szintre. Ezután az $a sztringemnek nem létező yyy tulajdonságára történő hivatkozás hibát okozott. Ez nagyon nagy szolgálat a hibakeresésben az 1.0 verzióhoz képest!

Ezen kívül van még egy szolgáltatása ennek a Set-StrictMode beállításnak. Ez pedig a hagyományos, Visual Basic-szerű függvényhívás szintaxisra való figyelmeztetés:

PS C:\Users\tibi> function valami ($x,$y){$x+$y}

PS C:\Users\tibi> valami(1,2)

The function or command was called as if it were a method. Parameters should b

e separated by spaces. For information about parameters, see the about_Paramet

ers Help topic.

At line:1 char:7

+ valami <<<< (1,2)

    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException

    + FullyQualifiedErrorId : StrictModeFunctionCallWithParens

Itt a nagyon egyszerű függvényemet úgy próbáltam hívni, mintha például egy Excel függvény, vagy egy metódus lenne: nincs szóköz a függvény neve és a zárójel között, és eleve zárójel van. Ez valószínűsíthetően hiba, így megkapjuk a hibajelzést.

Ássunk még mélyebbre (Trace-Command)

Ha még ez sem lenne elég, akkor még mélyebbre áshatunk a Trace-Command  cmdlet segítségével. Igazából ez nem biztos, hogy a rendszergazdák mindennapos eszköze lesz, inkább azon fejlesztők tudnak profitálni használatából, akik cmdleteket fejlesztenek.

Nézzük a szintaxisát:

[5] PS I:\>(get-help trace-command).syntax

 

Trace-Command [-Command] <string> [-ArgumentList <Object[]>] [-Name] <string[]

> [[-Option] {None | Constructor | Dispose | Finalizer | Method | Property | D

elegates | Events | Exception | Lock | Error | Errors | Warning | Verbose | Wr

iteLine | Data | Scope | ExecutionFlow | Assert | All}] [-Debugger] [-FilePath

 <string>] [-Force] [-InputObject <psobject>] [-ListenerOption {None | Logical

OperationStack | DateTime | Timestamp | ProcessId | ThreadId | Callstack}] [-P

SHost] [<CommonParameters>]

Trace-Command [-Expression] <scriptblock> [-Name] <string[]> [[-Option] {None

| Constructor | Dispose | Finalizer | Method | Property | Delegates | Events |

 Exception | Lock | Error | Errors | Warning | Verbose | WriteLine | Data | Sc

ope | ExecutionFlow | Assert | All}] [-Debugger] [-FilePath <string>] [-Force]

 [-InputObject <psobject>] [-ListenerOption {None | LogicalOperationStack | Da

teTime | Timestamp | ProcessId | ThreadId | Callstack}] [-PSHost] [<CommonPara

meters>]

Huh, nem túl egyszerű! Az látható, hogy alapvetően kétfajta dolgot lehet vele vizsgálni: parancsokat és kifejezéseket. Alapvetően mi (rendszergazdák) két esetben használhatjuk: az első eset, amikor a parancsok, kifejezések meghívásának körülményeit szeretnénk tisztázni, a másik, amikor a paraméterek átadásának körülményeit. Nézzünk példát az elsőre:

PS C:\> Trace-Command commanddiscovery {dir c:\} -pshost

DEBUG: CommandDiscovery Information: 0 : Looking up command: dir

DEBUG: CommandDiscovery Information: 0 : Alias found: dir  Get-ChildItem

DEBUG: CommandDiscovery Information: 0 : Cmdlet found: Get-ChildItem

Microsoft.PowerShell.Commands.GetChildItemCommand,

Microsoft.PowerShell.Commands.Management, Version=1.0.0.0, Culture=neutral,

PublicKeyToken=31bf3856ad364e35

 

 

    Directory: C:\

 

 

Mode                LastWriteTime     Length Name

----                -------------     ------ ----

d----       2009.07.14.      5:20            PerfLogs

d-r--       2010.02.13.     15:39            Program Files

d-r--       2010.02.25.     14:31            Program Files (x86)

Itt a trace-command cmdletet a „commanddiscovery” névvel hívtam meg. Mögötte ott van az a kifejezés szkriptblokk formában, amit elemeztetni szeretnék, majd megadom, hogy az eredményt a konzolra írja ki.

Az elemzés felderíti, hogy a PowerShell a „dir” álnevet hogyan párosítja a Get‑ChildItem cmdlettel, megnézi, hogy nem tréfáltuk-e meg, és nem készítettünk-e ilyen névvel valamilyen függvényt vagy szűrőt (filter). Ha nem, akkor kiírja, hogy melyik PSSnapIn-ben van ez a cmdlet definiálva és utána már csak a futásának eredményét látjuk. A trace-command ezzel az üzemmódjával tehát akkor jön jól nekünk, ha mindenféle aliast, függvényt, szkriptet és egyebeket definiálunk, gyanítjuk, hogy esetleg többször is ugyanolyan névvel, és amikor futtatunk valamit, nem értjük, hogy mi is fut le valójában.

Nézzük a paraméterátadás felderítését:

PS C:\> Trace-Command –name parameterbinding –expression {get-process powershell} -pshost

DEBUG: ParameterBinding Information: 0 : BIND NAMED cmd line args

[Get-Process]

DEBUG: ParameterBinding Information: 0 : BIND POSITIONAL cmd line args

[Get-Process]

DEBUG: ParameterBinding Information: 0 :     BIND arg [powershell] to

parameter [Name]

DEBUG: ParameterBinding Information: 0 :         Binding collection parameter

Name: argument type [String], parameter type [System.String[]], collection

type Array, element type [System.String], no coerceElementType

DEBUG: ParameterBinding Information: 0 :         Creating array with element

type [System.String] and 1 elements

DEBUG: ParameterBinding Information: 0 :         Argument type String is not

IList, treating this as scalar

DEBUG: ParameterBinding Information: 0 :         Adding scalar element of type

 String to array position 0

DEBUG: ParameterBinding Information: 0 :         Executing VALIDATION

metadata: [System.Management.Automation.ValidateNotNullOrEmptyAttribute]

DEBUG: ParameterBinding Information: 0 :         BIND arg [System.String[]] to

 param [Name] SUCCESSFUL

DEBUG: ParameterBinding Information: 0 : MANDATORY PARAMETER CHECK on cmdlet

[Get-Process]

DEBUG: ParameterBinding Information: 0 : CALLING BeginProcessing

DEBUG: ParameterBinding Information: 0 : CALLING EndProcessing

 

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName

-------  ------    -----      ----- -----   ------     -- -----------

    382      24    85184      90136   595     4,52   2912 powershell

Itt a trace-command név paramétere „parameterbinding”, mögötte megint a vizsgálandó kifejezés, majd az, hogy a konzolra küldje az eredményt. Látjuk, hogy a „PowerShell” karaktersorozatot a „Name” paraméterhez fűzte. Ez nekünk természetesnek tűnik, de azért ez nem ilyen triviális. Nézzük, vajon mit is vár a get‑processName” paraméter gyanánt:

[10] PS I:\>(get-help get-process).syntax

 

Get-Process [[-name] <string[]>] [<CommonParameters>]

Láthatjuk a súgóban, hogy egy sztringtömböt vár a get-process. Az elemzés további része annak lépéseit mutatja, hogy hogyan csinál a PowerShell a sima sztringből sztringtömböt, és hogy ezzel a paraméterrel sikeresen vette a cmdlet mindhárom feldolgozási fázisát is, a begin és az end részt is.

A másik eset, hogy nem értjük, hogy egy cmdletnek vagy szkriptnek átadott paraméter miért nem jó, miért nem úgy értelmezi a kifejezés, mint ahogy mi szeretnénk. Ez utóbbi demonstrálására nézzünk egy példát, amelyben annak nézünk utána, hogy ha egy gyűjteményt adunk át a get-member-nek, akkor az vajon miért a gyűjtemény elemeinek tagjellemzőit adja vissza, miért nem a gyűjteményét:

PS C:\> Trace-Command parameterbinding {1, "kettő" | gm} -pshost

DEBUG: ParameterBinding Information: 0 : BIND NAMED cmd line args [Get-Member]

DEBUG: ParameterBinding Information: 0 : BIND POSITIONAL cmd line args

[Get-Member]

DEBUG: ParameterBinding Information: 0 : MANDATORY PARAMETER CHECK on cmdlet

[Get-Member]

DEBUG: ParameterBinding Information: 0 : CALLING BeginProcessing

DEBUG: ParameterBinding Information: 0 : BIND PIPELINE object to parameters:

[Get-Member]

DEBUG: ParameterBinding Information: 0 :     PIPELINE object TYPE =

[System.Int32]

DEBUG: ParameterBinding Information: 0 :     RESTORING pipeline parameter's

original values

DEBUG: ParameterBinding Information: 0 :     Parameter [InputObject] PIPELINE

INPUT ValueFromPipeline NO COERCION

DEBUG: ParameterBinding Information: 0 :     BIND arg [1] to parameter

[InputObject]

DEBUG: ParameterBinding Information: 0 :         BIND arg [1] to param

[InputObject] SUCCESSFUL

DEBUG: ParameterBinding Information: 0 : MANDATORY PARAMETER CHECK on cmdlet

[Get-Member]

DEBUG: ParameterBinding Information: 0 : BIND PIPELINE object to parameters:

[Get-Member]

DEBUG: ParameterBinding Information: 0 :     PIPELINE object TYPE =

[System.String]

DEBUG: ParameterBinding Information: 0 :     RESTORING pipeline parameter's

original values

DEBUG: ParameterBinding Information: 0 :     Parameter [InputObject] PIPELINE

INPUT ValueFromPipeline NO COERCION

DEBUG: ParameterBinding Information: 0 :     BIND arg [kettő] to parameter

[InputObject]

DEBUG: ParameterBinding Information: 0 :         BIND arg [kettő] to param

[InputObject] SUCCESSFUL

DEBUG: ParameterBinding Information: 0 : MANDATORY PARAMETER CHECK on cmdlet

[Get-Member]

DEBUG: ParameterBinding Information: 0 : CALLING EndProcessing

Láthatjuk, hogy a get-member hogyan dolgozza fel a csővezetéken érkező különböző objektumokat, hogyan emelődnek át az argumentumok InputObject-té. Elsőként a parancsértelmező megnézi, hogy vannak-e név szerinti paraméterek a parancssorban, majd ellenőrzi a pozíció alapján történő paraméterátadást. Ezután ellenőrzi, hogy a kötelező paraméterek meg vannak-e adva, a get-member-nek nincs ilyenje. Mindezek után veszi észre, hogy csővezetékben adom át a paramétereket, így annak kezelése történik meg, először az „1” lesz behelyettesítve „InputObject” paraméterként, majd a „kettő”.

Megszakítási pontok kezelése a konzolon

A PowerShell 2.0-ban jelent meg beépített, nyelvi szinten definiált (azaz nem a szerkesztő eszköz vagy a konzol szintjén definiált) megszakítási pontok létrehozásának lehetősége. Ilyen megszakítási pontokat a PSBreakPoint főnévvel rendelkező cmdletekkel lehet kezelni:

[16] PS C:\> Get-Command -Noun psbreakpoint

 

CommandType     Name                            Definition

-----------     ----                            ----------

Cmdlet          Disable-PSBreakpoint            Disable-PSBreakpoint [-Brea...

Cmdlet          Enable-PSBreakpoint             Enable-PSBreakpoint [-Id] <...

Cmdlet          Get-PSBreakpoint                Get-PSBreakpoint [[-Script]...

Cmdlet          Remove-PSBreakpoint             Remove-PSBreakpoint [-Break...

Cmdlet          Set-PSBreakpoint                Set-PSBreakpoint [-Script] ...

Ezeket vagy szkriptek szintjén lehet kezelni, ha pozícióhoz (sor, oszlop) rendeljük a megszakítást, vagy a konzol ablakban is használhatjuk ad-hoc módon, ha parancsokhoz vagy változóhoz rendeljük ezeket.

Az első lépés a megszakítási pont létrehozása, ehhez a Set-PSBreakPoint  cmdletet használhatjuk. Nézzük ennek szintaxisait:

[17] PS C:\> (get-help Set-PSBreakpoint).syntax

 

Set-PSBreakpoint -Command <string[]> [[-Script] <string[]>] [-Action <scriptbl

ock>] [<CommonParameters>]

Set-PSBreakpoint [-Script] <string[]> [-Line] <Int32[]> [[-Column] <int>] [-Ac

tion <scriptblock>] [<CommonParameters>]

Set-PSBreakpoint -Variable <string[]> [[-Script] <string[]>] [-Mode {Read | Wr

ite | ReadWrite}] [-Action <scriptblock>] [<CommonParameters>]

Nézzük az utolsó opciót, amikor is változót figyelünk. Látható, hogy három üzemmód van: olvasást, írást vagy mindkettőt figyeljük. Az alapérték az írás figyelése:

PS C:\> Set-PSBreakpoint -Variable a

 

  ID Script            Line Command          Variable         Action

  -- ------            ---- -------          --------         ------

   0                                         a

 

 

PS C:\> $a = 8

Entering debug mode. Use h or ? for help.

 

Hit Variable breakpoint on '$a' (Write access)

 

$a = 8

[DBG]: PS C:\>>> ?

 

 s, stepInto         Single step (step into functions, scripts, etc.)

 v, stepOver         Step to next statement (step over functions, scripts, etc.

)

 o, stepOut          Step out of the current function, script, etc.

 

 c, continue         Continue execution

 q, quit             Stop execution and exit the debugger

 

 k, Get-PSCallStack  Display call stack

 

 l, list             List source code for the current script.

                     Use "list" to start from the current line, "list <m>"

                     to start from line <m>, and "list <m> <n>" to list <n>

                     lines starting from line <m>

 

 <enter>             Repeat last command if it was stepInto, stepOver or list

 

 ?, h                Displays this help message

 

 

For instructions about how to customize your debugger prompt, type "help about_

prompt".

 

[DBG]: PS C:\>>>

A fenti példában az „a” változó manipulálására kérek megszakítást a [13]-as sorban. Majd értéket adok $a-nak, erre rögtön debug, azaz hibakereső üzemmódba lép a prompt. Itt számos parancs áll rendelkezésünkre, amelyeket a kérdőjellel ki is listáztam. Látható, hogy megváltozott a prompt is, a sor elején a [DBG] előtag látható. Ebben a promptban bármit csinálhatunk, futtathatunk parancsokat vagy a fenti, speciális hibakezelő parancsokat alkalmazhatjuk.

Ha most kiolvasom a $a értékét, akkor a következőket látom:

[DBG]: PS C:\>>> $a

1

A [14]-es sorral ellentétben az $a értéke még a korábbi, azaz a megszakítási pontok mindig a feltétel megjelenése előtti ponton állítják le a futást. Kilépni a C, azaz Continue-val lehet ebből az üzemmódból. A megszakítási pontokat megszűntetni a következő kifejezéssel lehet:

[23] PS C:\> Get-PSBreakpoint | Remove-PSBreakpoint

A PSBreakPointok is „scope”-pal rendelkeznek, azaz ha egy olyan függvényt vagy szkriptet futtatok, amelyben szintén van $a változó, akkor a felsőbb scope-okban definiált megszakítási pontok erre is hatni fognak. Ennek bemutatására egy kis előkészítést végeztem:

[38] PS C:\> $a = 123

[39] PS C:\> function belső ($a){"Ez egy belső a: $a"}

[40] PS C:\> Set-PSBreakpoint -Variable a -Mode readwrite

 

  ID Script            Line Command          Variable         Action

  -- ------            ---- -------          --------         ------

   6                                         a

Van tehát egy $a külső változóm, egy „belső” nevű függvényem, amiben van egy belső $a változó, majd definiálok egy megszakítási pontot az „a” változóhoz mind írásra, mind olvasásra. Nézzük, mi történik, ha meghívom a belső függvényt:

[41] PS C:\> belső 8

Hit Variable breakpoint on '$a' (ReadWrite access)

 

function belső ($a){"Ez egy belső a: $a"}

[DBG]: PS C:\>>> $a

8

[DBG]: PS C:\>>> $global:a

123

A megszakítási pont feléledt, beléptem a hibakeresési üzemmódba. Az $a-t kiolvasva megkaptam a paraméterátadás uráni 8-at, azaz a hibakeresés scope-ja a függvény. Viszont elérem akár a külső $a-t is a $global:a formátum segítségével.

Azt, hogy pontosan hol is vagyok, a „k”, azaz Get-PSCallStack  belső paranccsal tudom megjeleníteni:

[DBG]: PS C:\>>> k

 

Command                    Arguments                 Location

-------                    ---------                 --------

belső                      {a=8}                     prompt

prompt                     {}                        prompt

Ezt a listát alulról fölfele kell értelmezni, azaz a prompton (azaz konzolon) belül vagyok, azon belül a belső függvényben. A Location oszlopban prompt szerepel, hiszen a függvényem is a promptban (konzolon) született, nem pedig egy fájlban van.

Nézzünk még érdekesebb megszakítási pontot, azt a fajtát, ahol az parancshoz van rendelve. Ennek kivesézéséhez nézzünk egy rekurzív függvényt, ami faktoriálist számol:

[43] PS C:\> function factor ($i)

>> {

>>     if($i -gt 2){

>>         $i* (factor ($i-1))

>>         }

>>     else {$i}

>> }

>> 

[44] PS C:\> Set-PSBreakpoint -Command factor

 

  ID Script            Line Command          Variable         Action

  -- ------            ---- -------          --------         ------

   7                        factor

 

 

[45] PS C:\> factor 6

Hit Command breakpoint on 'factor'

 

factor 6

[DBG]: PS C:\>>> $i

[DBG]: PS C:\>>>

Ugye a faktoriálist úgy lehet számolni, hogy ha a paraméter nagyobb, mint 2, akkor a faktoriális az úgy képezhető, hogy az aktuális szám szorozva az eggyel kisebb szám faktoriálisa. Ha pedig a szám 2 vagy kisebb, akkor maga a szám. Ezután felállítottam a megszakítási pontot, hozzárendelve a „factor” nevű parancshoz, ami a függvényem. Majd meghívtam ezt, 6 faktoriálisát keresem. Rögtön meg is jelent a megszakítás. Kiolvasva a függvényem belső $i változóját azonban nem kaptam semmit, hiszen én még „kívül” vagyok. Ezt a hívási lista ellenőrzésével is láthatom:

[DBG]: PS C:\>>> k

 

Command                    Arguments                 Location

-------                    ---------                 --------

prompt                     {}                        prompt

Azaz én még mindig a konzolon vagyok, ott meg nincs $i változó definiálva. Belelépni a függvénybe az „s”, azaz stepInto paranccsal lehet:

[DBG]: PS C:\>>> s

    if($i -gt 2){

[DBG]: PS C:\>>> $i

6

Itt már van $i. Folytatni lehet a „c”, azaz „Continue”, folytatással:

[DBG]: PS C:\>>> c

Hit Command breakpoint on 'factor'

 

        $i* (factor ($i-1))

[DBG]: PS C:\>>> k

 

Command                    Arguments                 Location

-------                    ---------                 --------

factor                     {i=6}                     prompt

prompt                     {}                        prompt

Ha hosszabb a szkriptünk és nem teljesen egyértelmű, hogy hol is vagyunk, akkor kérhetjük a forráskód megjelenítését az „l”, azaz List paranccsal:

[DBG]: PS C:\>>> l

 

    1:  function factor ($i)

    2:  {

    3:*     if($i -gt 2){

    4:          $i* (factor ($i-1))

    5:          }

    6:      else {$i}

    7:  }

Ha elég sokat folytatjuk a felderítést, akkor akár ilyen hívási mélységet is elérünk:

[DBG]: PS C:\>>> k

 

Command                    Arguments                 Location

-------                    ---------                 --------

factor                     {i=2}                     prompt

factor                     {i=3}                     prompt

factor                     {i=4}                     prompt

factor                     {i=5}                     prompt

factor                     {i=6}                     prompt

prompt                     {}                        prompt

Miután nagyon rövidke a függvényem, így a lépésenkénti végrehajtás: „s” – stepInto, a továbblépés az adott szinten: „v”, stepOver és a kilépés az adott szintről: „o”, stepOut lehetőségek között nincs sok látványbeli különbség.

Nézzünk még egy lehetőséget, a megszakítási pontokhoz valamilyen szkriptblokkot is rendelhetünk a debug prompt helyett:

[56] PS C:\> Set-PSBreakpoint -Command factor -Action {write-host "Most az i: $

i"}

 

  ID Script            Line Command          Variable         Action

  -- ------            ---- -------          --------         ------

   9                        factor                            write-host "M...

 

 

[57] PS C:\> factor 5

Most az i:

Most az i: 5

Most az i: 4

Most az i: 3

120

Ezzel tehát nem szakad meg a futás, hanem a megszakítási feltétel teljesülésekor lefut az –Action paraméternek átadott szkriptblokk.

Utoljára hagytam a szerintem legkevésbé érdekes lehetőséget, amikor is a megszakítási pontot a szkriptünk valamely pozíciójához rendeljük. Íme, a szkriptem:

87 . ábra A fact.ps1 szkript

Ennek a szkriptnek az 5. sorához rendelem a megszakítási pontot:

[59] PS C:\> Set-PSBreakpoint -Script C:\munka\fact.ps1 -Line 5

 

  ID Script            Line Command          Variable         Action

  -- ------            ---- -------          --------         ------

  10 fact.ps1             5

[60] PS C:\> C:\munka\fact.ps1 5

Hit Line breakpoint on 'C:\munka\fact.ps1:5'

 

fact.ps1:5           $i* (factor ($i-1))

[DBG]: PS C:\>>> $i

5

[DBG]: PS C:\>>> v

Hit Line breakpoint on 'C:\munka\fact.ps1:5'

 

fact.ps1:5           $i* (factor ($i-1))

[DBG]: PS C:\>>> $i

4

[DBG]: PS C:\>>> o

Hit Line breakpoint on 'C:\munka\fact.ps1:5'

 

fact.ps1:5           $i* (factor ($i-1))

[DBG]: PS C:\>>> c

120

Látható, hogy ugyanúgy működik a folyamat, mint a többi esetben.

Egy szkripthez akár több és többfajta megszakítási pontot is rendelhetünk, illetve az –Action részbe intelligenciákat helyezhetünk, amivel önműködően tudjuk vezérelni a hibakeresést:

[62] PS C:\> Set-PSBreakpoint -Script C:\munka\fact.ps1 -Line 5 -Action {if($i

-gt 3){Write-Host "i: $i"}else{continue} }

 

  ID Script            Line Command          Variable         Action

  -- ------            ---- -------          --------         ------

  11 fact.ps1             5                                   if($i -gt 3){...

 

 

[63] PS C:\> C:\munka\fact.ps1 5

i: 5

i: 4

120

Ebben a példában automatikusan továbblép a futás, ha az $i értéke kisebb egyenlő, mint 3. A PSBreakPoint-okat még hatástalaníthatjuk, és újra élesíthetjük a Disabe-PSBreakpoint  és az Enable‑PSBreakpoint  cmdletekkel.

Az „igazi” megszakítási pontokat csak elmentett szkriptek esetén használhatjuk. Azonban van még egy praktikus lehetőség a programunk megszakítására ad-hoc kifejezéseink tesztelésére, főleg a grafikus felületen:

$host.EnterNestedPrompt()

Például:

88 . ábra Egyedi megszakítási pont beillesztése

Látható, hogy az első sor után bejutottam egy alpromtba, ahol bármilyen műveletet végre tudunk hajtani, majd az Exit kulcsszóval lehet folytatni a kódunk futtatását.

A PowerShell 5.0-ban megjelent egy új cmdlet, a Wait-Debugger , amellyel az előző EnterNestedPrompt()-nál is fejlettebb, a megszakítási pontokkal azonosan működö hibakaresési lehetőséghez jutunk. Az alábbi példában egy hatsoros kifejezést másoltam be a konzolba, amelyben a 3. sorban van egy Wait-Debugger cmdlet:

PS C:\> Write-Host eleje

>>> $a = 1

>>> Wait-Debugger

>>> $a

>>> get-date

>>> Write-Host vége

eleje

At line:4 char:1

+ $a

+ ~~

[DBG]: PS C:\>>

 Látható, hogy ennek hatására a 4. sornál megállt a végrehajtás és egy alpromptba jutottam, ahol a szokásos konzolbeli egybetűs parancsokat adhatom ki a hibafelderítéshez:

[DBG]: PS C:\>> l

 

 

    1:  Write-Host eleje

    2:  $a = 1

    3:  Wait-Debugger

    4:* $a

    5:  get-date

    6:  Write-Host vége

 

 

[DBG]: PS C:\>> v

1

At line:5 char:1

+ get-date

+ ~~~~~~~~

[DBG]: PS C:\>> $a

1

[DBG]: PS C:\>> c

 

2015. szeptember 19. 17:27:53

vége

Megszakítási pontok kezelése a grafikus szerkesztőben

Ha már hibakeresésre fanyalodunk, akkor valószínű soksoros szkriptekkel dolgozunk, amelyeket a grafikus felületen könnyebb készíteni. Így érdemes megnézni, hogy itt hogyan lehet ezekkel a lehetőségekkel élni. A PowerShell ISE felületén a fenti lehetőségek egy része a menübe ki van vezetve:

89 . ábra A PowerShell ISE hibakeresési lehetőségei

A sorokhoz kötött megszakítási pontot könnyű elhelyezni és kezelni. Parancshoz vagy változóhoz ugyanúgy kell, mint a konzolon. Ha már belekerültünk a hibakeresési üzemmódba, akkor különböző gyorsbillentyűkkel lehet vezérelni a továbblépést.

Szkriptek és kifejezések hibakeresése megszakítási pontok nélkül

PowerShell 5.0 ISE-ben jelent meg a „Break All” lehetőség (Ctr + B), amellyel megállítható az éppen futó szkript és ugyanúgy folyhat a hibafelderítés, mintha megszakítási pontot helyeztünk volna el. Ezzel a gyanúsan sokáig futó szkriptjeinket tudjuk megállítani és belepillantani az eseményekbe.

Ugyan ezzel a módszerrel elmentetlen „szkripteket” is meg tudunk állítani, természetesen itt is több szolgáltatást kapunk, ha a szkriptünk el van mentve, hiszen ilyenkor az éppen aktuális sor sárga hátteret kap:

90 . ábra Megállítás megszakítási pont nélkül Ctrl+B-vel

A fenti példában a végtelen ciklusomat elsőként még elmentetlenül állítottam meg, ilyenkor világoskékkel az adott sor megjelenik a kimeneti ablakban. Ellenben, ha a szkriptünk el van mentve, akkor látható, hogy a sárga háttérrel a szkriptszerkesztő részben az aktuális sor ki van emelve. Azaz Ctrl+B  hatására a futás megállt, hibakereső üzemmódba került az ISE és mindaz a lehetőség rendelkezésünkre áll, mint ami a megszakítási pontok elérésekor is elérhető, azaz kiolvashatjuk az aktuális változókat, léptethetjük a futtatást soronként, stb.

Ugyanez a konzolon is elérhető, csak itt nem a Ctrl+B-t kell megnyomni, hanem a Ctrl+Break -et:

PS C:\> while($true){get-date;Start-Sleep 2}

 

2015. október 4. 18:23:50

2015. október 4. 18:23:52

Entering debug mode. Use h or ? for help.

 

At line:1 char:7

+ while($true){get-date;Start-Sleep 2}

+       ~~~~~

[DBG]: PS C:\>> $a

[DBG]: PS C:\>> exit

2015. október 4. 18:24:25

2015. október 4. 18:24:27

Visszatérni a futáshoz akár a hagyományos exit paranccsal tudunk.



Word To HTML Converter