PowerShell 5.0-nál korábbi verziók esetében nem volt könnyű dolgunk háttérfolyamatként futó vagy távoli módon futtatott szkriptek hibafelderítésében. Az általános módszer az volt, hogy a háttérben vagy távol futó szkripteket előtérben futtattuk, és ott használtuk a megszakítási pontokat és egyéb lehetőségeket. Ez akkor volt problematikus, amikor pont a paraméterátadással vagy az eredmények átvételével kapcsolatban gyanakodtunk hibákra, hiszen a normál módon futtatott szkriptekkel az így előforduló hibalehetőségek nem voltak szimulálhatók.
PowerShell 5.0-ban már teljesértékű módon, akár az ISE grafikus szerkesztőben is könnyen kereshetjük a hibákat.
Háttérfolyamatok esetében nagyon egyszerű a dolgunk. Nézzünk egy nagyon egyszerű példát:
$script = {
$a = "valami"
Wait-Debugger
Write-Host "haliho"
}
Start-Job -ScriptBlock $script
Látható, hogy itt megszakítási pont helyett a Wait-Debugger cmdlettel szakítom meg a futtatást. Ha ezt elindítom, majd lekérdezem a futó háttérfolyamatokat, akkor ezt látom:
PS C:\PowerShell> Get-Job
Id Name PSJobTypeName State HasMoreData Location
-- ---- ------------- ----- ----------- --------
10 Job10 BackgroundJob AtBreakpoint True localhost
Egy új státus jelent meg, az „AtBreakpoint”, ami azt mutatja, hogy az adott háttérfolyamat a hibakeresésre vár. Ezt indítani a Debug-Job cmdlettel tudjuk:
PS C:\PowerShell> Debug-Job -Id 10
Stopped at: Write-Host "haliho"
És innentől használhatjuk a továbblépés, folytatás és egyéb lehetőségeket. Még látványosabb a dolog, ha a héttérfolyamat szkrtiptblokkját elmentjük fájlként, és azt futtatjuk háttérfolyamatként:
Set-Content -Path C:\PowerShell\job.ps1 -Value $script
Start-Job -ScriptBlock {C:\PowerShell\job.ps1}
Itt szintén megáll a futtatás és szintén el kell indítani a debugger-t:
PS C:\PowerShell> Debug-Job -Id 12
De ilyenkor automatikusan betöltődik az elmentett szkript az ISE-ben egy újabb fülön és megjelenik a sárga héttérrel kiemelt sor:
100 . ábra Elmentett szkript háttérben futtatásakor teljes értékű a hibakeresés
Azért még mindig lehetne fokozni a hibakeresési élményt azzal, hogy az előtérben történő lépésenkénti végrehajtásnál át tudnánk lépni a Start-Job sorából a háttérben futó szkript lépésenkénti végrehajtásába, de ez sajnos még nem megy, muszáj Wait-Debugger-t tenni oda.
Az előző fejezetben látott „BreakAll” funkciót szintén ellátja a Debug-Job cmdlet. Ha a háttérfolyamatunk hosszabb ideig fut és nem raktunk be Wait-Debugger-t, akkor a Debug-Job erőszakkal is megállítja a háttérfolyamatot és belép hibakeresési módba.
Ez a hibakeresés akkor is működik, ha nem is én indítottam el a PowerShell processzt. A Get-PSHostProcessInfo cmdlet segítségével fel tudom mérni, hogy melyek azok a processzek, amik a PowerShell-t futtatják, lehetnek ezek akár háttérfolyamatok (job), powershell.exe vagy ISE, akármi. Most épp egy ISE a folyamat, amit szeretnék elemezgetni:
PS C:\> Get-PSHostProcessInfo
ProcessName ProcessId AppDomainName MainWindowTitle
----------- --------- ------------- ---------------
powershell 8020 DefaultAppDomain Windows PowerShell
powershell_ise 19804 DefaultAppDomain Windows PowerShell ISE
Ennek az ISE folyamatnak az azonosítója 19804. Hozzá tudok csatlakozni ehhez a processzhez az Enter-PSHostProcess segítségével:
PS C:\> Enter-PSHostProcess -Id 19804
[Process:19804]: PS C:\Users\Tibi\Documents> Get-Runspace
Ilyenkor a prompt is megváltozik. Ilyenkor mi nem ugyanabba a fő runspace-be csatlakozunk be, hanem egy új nyílik számunkra. Ezeket a runspace-eket felmérhetjük:
[Process:19804]: PS C:\Users\Tibi\Documents> Get-Runspace
Id Name ComputerName Type State Availability
-- ---- ------------ ---- ----- ------------
1 Runspace1 localhost Local Opened Busy
2 RemoteHost localhost Local Opened Busy
A RemoteHost nevű runspace az, ami most megnyílt számunkra, tehát abban vagyok épp most benne. Ha szeretném az ISE-ben futó szkriptemet debugolni, akkor a 1-esbe kell belépjek a Debug-Runspace cmdlettel:
[Process:19804]: PS C:\Users\Tibi\Documents> Debug-Runspace -Id 1
Debugging Runspace: Runspace1
To end the debugging session type the 'Detach' command at the debugger prompt,
or type 'Ctrl+C' otherwise.
At line:1 char:7
+ while($true){Get-Random; Start-Sleep 1}
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Itt mutatja is, hogy hol állt éppen meg a szkriptem, tudnék a szokásos debug parancsokkal operálni. Ha a $PID változót megnézem, akkor látszik, hogy tényleg az ISE processzben vagyok benne:
[DBG]: [Process:19804]: [Runspace1]: PS Course:\>> $PID
19804
Ha kész vagyok a hibakereséssel, akkor a detach paranccsal léphetek ki a debug runspace-ből, ilyenkor folytatódik a szkript futása a háttérben, majd az exit-tel a processzből:
[DBG]: [Process:19804]: [Runspace1]: PS Course:\>> detach
[Process:19804]: PS C:\Users\Tibi\Documents> exit
PS C:\>
Csak egy kicsit bonyolultabb a helyzetünk távoli munkamenetek hibakeresésekor. Induljunk ki hasonló helyzetből, mint az előzőekben látott, elmentett szkript-alapú példa volt, méghozzá tervezett hibafelderítést végzünk a Wait-Debugger cmdlet használatával:
$rs = {
$pid
$a = 1
Wait-Debugger
Get-date
}
Set-Content -Path $env:TEMP\remscr.ps1 -Value $rs
$s = New-PSSession -ComputerName dc2016
Copy-Item -ToSession $s -Path $env:TEMP\remscr.ps1 -Destination c:\powershell
Invoke-Command -Session $s -ScriptBlock {& c:\powershell\remscr.ps1}
Itt az $rs változóban tárolom a távoli futtatásra szánt szkriptet. Ennek első sorában található a $pid változó kiíratása, mert ez adja meg nekünk a távoli PowerShell processz azonosítóját és erre szükségünk lenne abban az esetben, ha nem rendelkeznénk a hibafelderítéshez szükséges sessionID-val. Ilyen példánk lesz a 2., ezt tehát mindenképpen érdemes valamilyen módon kinyerni a futtatás során.
Ezt a szkriptet elmentem az ideiglenes mappába, majd megnyitok egy kapcsolatot a távoli géphez. A szkriptet – kihasználva a PowerShell 5.0 új lehetőségét – ezen a távoli kapcsolaton keresztül másolom át a DC2016 gépem c:\powershell mappájába. Végén meghívom távoli végrehajtással ezt a szkriptet.
3228
WARNING: Session Session1 with instance ID bbd0e04b-7286-4a0b-be2c-cdda7a3bfab5 on computer dc2016 has been disconnected because the script running on the session has stopped at a breakpoint.
Use the Enter-PSSession cmdlet on this session to connect back to the session and begin interactive debugging.
Láthatjuk, hogy a processzazonosító 3228 és hogy a távoli kapcsolat bontódott is, mert a távoli szkript hibakereső állapotba került. A session egyedi azonosítója (instanceID) látható is a figyelmeztető üzenetben, erre is szükségünk lehet.
Most tulajdonképpen bármelyik gépről folytathatjuk a hibafelderítést, egyszerűség kedvéért folytassuk ugyanazon a gépen, annál is inkább, hiszen a session objektum még mindig megtalálható a $s változóban, így egyszerűen újra tudunk ehhez csatlakozni, majd be is léphetünk ebbe a munkamenetbe:
PS C:\Windows\system32> Connect-PSSession -Session $s
Id Name ComputerName State ConfigurationName Availability
-- ---- ------------ ----- ----------------- ------------
1 Session1 dc2016 Opened Microsoft.PowerShell RemoteDebug
PS C:\Windows\system32> Enter-PSSession -Session $s
WARNING: You have entered a session that is currently stopped at a debug breakpoint inside a running command or script. Use the Windows PowerShell command line debugger to continue debugging.
[dc2016]: [DBG]: PS C:\Users\Administrator\Documents>>
Ilyenkor az ISE a PowerShell 5.0-ban – miután egy elmentett szkriptről van szó – meg is nyitja ezt egy új szkriptszerkesztő fülön:
101 . ábra A távoli szkript automatikusan megnyílik az ISE szerkesztőjében
Látható a fül szövegében, hogy ez egy távoli szkript ([Remote File]), de ettől függetlenül a hibafelderítés minden lehetősége rendelkezésünkre áll, mint például a lépésenkénti végrehajtás, vagy a változók tartalmának kiolvasása.
Most nézzük azt az esetet, amikor egy hosszan futó szkriptünk van, és nem tervezett módon akarunk hibafelderítést végezni. Ehhez a módosított szkript a következő:
$rs = {
$pid
$a = 1
while($true){
Get-date
Start-Sleep 5
}
}
Set-Content -Path $env:TEMP\remscr.ps1 -Value $rs
$s = New-PSSession -ComputerName dc2016
Copy-Item -ToSession $s -Path $env:TEMP\remscr.ps1 -Destination c:\powershell
Invoke-Command -Session $s -ScriptBlock {& c:\powershell\remscr.ps1}
Az első sorban a futó processz azonosítóját, a végtelen ciklusban az aktuális időt írom ki. Itt is elmentem a szkriptet és a távoli kapcsolaton keresztül másolom át a DC2016 nevű gépemre.
Ha ezt elindítom, meg is kapom a processz azonosítót és az időadatok elkezdenek csordogálni:
3176
2015. szeptember 19., szombat 23:50:36
2015. szeptember 19., szombat 23:50:41
2015. szeptember 19., szombat 23:50:46
Nézzük most azt a helyzetet, mintha nem férnénk hozzá a session azonosítójához. Ez némileg igaz is, hiszen fut a szkriptünk így most nem tudunk belenézni a $s tartalmába, és nem akarjuk megállítani. Nyitok egy új Powershell Tab-ot az ISE-ben:
102 . ábra New PowerShell Tab - új PowerShell processz ugyanabban az ISE ablakban
És itt futtatom a következő cmdleteket:
PS C:\Windows\system32> Enter-PSSession -ComputerName dc2016
[dc2016]: PS C:\Users\Administrator\Documents> Enter-PSHostProcess -id 3176
[dc2016]: [Process:3176]: PS C:\Users\Administrator\Documents> Get-Runspace
Id Name ComputerName Type State Availability
-- ---- ------------ ---- ----- ------------
1 RemoteHost localhost Local Opened Busy
2 RemoteHost localhost Local Opened Busy
[dc2016]: [Process:3176]: PS C:\Users\Administrator\Documents> Debug-Runspace 1
[dc2016]: [DBG]: [Process:3176]: [RemoteHost]: PS C:\Users\Administrator\Documents>>
Első lépésben tehát csatlakoztam a DC2016 gépemhez, majd az előbb látott 3176-os processzhez csatlakozom az új Enter-PSHostProcess cmdlettel. Látható, hogy a sor elején, a promptban a [dc2016] mellett immár a [Process:3176] is megjelent. Még nem értünk teljesen célt, hiszen a processz egy új futtatási környezetébe (másik szál) érkeztünk, a hibakeresést az 1-es számú Runspace-ben kell végezzük, így oda csatlakozom a Debug-Runspace 1 cmdlettel. Ilyenkor megint megnyílik a szkriptszerkesztőben a távoli szkript és sárga kiemeléssel mutatja, hogy éppen melyik sornál állítottuk meg a futtatást.
Ha végeztem a hibafelderítéssel és szeretném, hogy a szkript futása folytatódjon, akkor az új detach paranccsal bezárhatom ezt az üzemmódot, majd exit-el kiléphetek a távoli kapcsolatból:
[dc2016]: [DBG]: [Process:2480]: [RemoteHost]: PS C:\Users\Administrator>> detach
[dc2016]: [Process:2480]: PS C:\Users\Administrator\Documents> exit
PS C:\Windows\system32>