Futtatás háttérben

A PowerShell 2.0 egyik újdonsága, hogy képes kifejezéseket háttérben futtatni. Korábban, ha egy időigényes kifejezést futtattunk, akkor a konzolunk addig „használhatatlan” volt, amíg a futó program be nem fejezte a ténykedését. Most viszont nyithatunk külön munkamenetet a hosszadalmas tevékenységek számára és a konzolunkon tovább dolgozhatunk. Eközben lekérdezhetjük a háttérben futó programunk státusát, és megtekinthetjük a kimenetét. Nézzük mindezt meg a gyakorlatban!

Az ezzel kapcsolatos cmdletek főneve a „Job”:

PS C:\> Get-Command -noun job

 

CommandType     Name                          Definition

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

Cmdlet          Get-Job                       Get-Job [[-Id] <Int32[]>] ...

Cmdlet          Receive-Job                   Receive-Job [-Job] <Job[]>...

Cmdlet          Remove-Job                    Remove-Job [[-Id] <Int32[]...

Cmdlet          Start-Job                     Start-Job [-ScriptBlock] <...

Cmdlet          Stop-Job                      Stop-Job [[-Id] <Int32[]>]...

Cmdlet          Wait-Job                      Wait-Job [[-Id] <Int32[]>]...

A munkát a Start-Job -bal kezdhetjük el:

PS C:\> start-job -Name Háttérmunka -ScriptBlock {get-process}

 

WARNING: column "Command" does not fit into the display and was removed.

 

Id              Name            State      HasMoreData     Location

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

3               Háttérmunka     Running    True            localhost

Látható, hogy adhatunk a munkamenetnek egy nevet és a ScriptBlock paraméterként átadott futtatható parancssort elindítja a PowerShell egy külön menetben. A Start-Job visszatérési értéke maga a „job” objektum. Ennek egyik legfontosabb tulajdonsága a State, azaz a státus. Rögtön indítás után természetesen ez még azt mutatja, hogy Running, azaz futó állapotban van.

Ha később újra le akarjuk kérdezni ennek a munkamenetnek az állapotát, akkor a get-job cmdlettel ezt megtehetjük:

PS C:\> get-job -Name Háttérmunka

 

WARNING: column "Command" does not fit into the display and was removed.

 

Id              Name            State      HasMoreData     Location

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

3               Háttérmunka     Completed  True            localhost

Ha azt látjuk, hogy a munkamenet tulajdonságai között a HasMoreData értéke $true, akkor megnézhetjük annak kimenetét is a Receive-Job  cmdlet segítségével:

PS C:\> Receive-Job -Name Háttérmunka

 

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

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

     34       2      568       2132    17     0,04   2548 conhost

     87       4     2016       5976    59     1,18   3148 conhost

    378       5     1160       1308    31     0,59    360 csrss

    202       6     1468       2296   158     1,19    404 csrss

     78       4      912       3320    20            2132 dllhost

     72       4     1056        864    37     0,11    980 dwm

...

Ha ezután újra megnézzük a munkamenet tulajdonságait és az már korábban lefutott, és nem lett újabb kimenetünk, akkor a HasMoreData értéke $false lesz.

Ha egy futó munkamenethez kapcsolódunk a Receive-Job segítségével, akkor az aktuális kimenetet kapjuk meg folyamatosan a konzolra, de természetesen egy Ctrl+C billentyűzetkombinációval ez megszakítható – de a munkamenet maga fut tovább! - és később újra „belepillanthatunk” a folyamatba. Ilyenkor az előző Receive‑Job óta generálódott kimenetet kapjuk meg, szintén HasMoreData = $true mellett:

PS C:\> start-job -ScriptBlock { for($i=0; $i -lt 60; $i++){$i; Start-Sleep

-Seconds 1}}

 

WARNING: column "Command" does not fit into the display and was removed.

 

Id              Name            State      HasMoreData     Location

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

13              Job13           Running    True            localhost

 

 

PS C:\> Receive-Job 13

0

1

2

3

4

5

6

7

PS C:\> Receive-Job 13

8

9

10

Ha lefutott a munkamenet, akkor a teljes kimenetet újra megnézhetjük a Receive‑Job segítségével. Szintén újra megkaphatunk egy már korábban kinyert kimenetet, ha a receive-job cmdletet a –keep kapcsolójával használjuk:

PS C:\> start-job -ScriptBlock { for($i=0; $i -lt 60; $i++){$i; Start-Sleep

-Seconds 1}}

 

WARNING: column "Command" does not fit into the display and was removed.

 

Id              Name            State      HasMoreData     Location

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

38              Job38           Running    True            localhost

 

PS C:\> receive-job -id 38 -keep

0

1

2

PS C:\> receive-job -id 38 -keep

0

1

2

3

4

Ha kifejezetten az a célunk, hogy egy munkamenet végét megvárjuk, akkor használhatjuk a Wait-Job  cmdletet:

PS C:\> start-job -Name Hosszú -ScriptBlock {Get-ChildItem c:\ -Recurse}

 

WARNING: column "Command" does not fit into the display and was removed.

 

Id              Name            State      HasMoreData     Location

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

5               Hosszú          Running    True            localhost

PS C:\> Wait-Job -Id 5

A Wait-Job után alaphelyzetben csak akkor kapjuk vissza a promptot, ha az adott munkamenet befejeződött.

Egy-egy munkamenet komoly memóriamennyiséget is lefoglalhat, mint például a fenti teljes C meghajtó fájljainak a kilistázása. Ha már nincs szükségünk az eredményre, akkor érdemes a Remove-Job  cmdlettel eltávolítani a munkamenetet:

PS C:\> Remove-Job -Id 5

Ha futtatjuk a munkamenetet, de mégsem akarjuk kivárni a befejeződését, akkor megszakíthatjuk a működését a Stop-Job  cmdlet segítségével:

PS C:\> start-job -Name Hosszú -ScriptBlock {Get-ChildItem c:\ -Recurse}

 

WARNING: column "Command" does not fit into the display and was removed.

 

Id              Name            State      HasMoreData     Location

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

7               Hosszú          Running    True            localhost

 

 

PS C:\> Stop-Job -id 7

PS C:\> Get-Job

 

WARNING: column "Command" does not fit into the display and was removed.

 

Id              Name            State      HasMoreData     Location

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

7               Hosszú          Stopped    False           localhost

Hogyan lehet paramétert átadni egy ilyen háttérben futó folyamatnak?

[3] PS C:\> $keresendőfájl = "szöveg.txt"

Létrehoztam egy változót, amiben egy keresendő fájl nevét tettem.

[4] PS C:\> $job = Start-Job -Name "Keres" -ScriptBlock {Get-ChildItem c:\

munka\$keresendőfájl -Recurse}

[5] PS C:\> $job

 

WARNING: column "Command" does not fit into the display and was removed.

 

Id              Name            State      HasMoreData     Location

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

17              Keres           Completed  True            localhost

 

[6] PS C:\> Receive-Job 17

 

 

    Directory: C:\munka

 

 

Mode                LastWriteTime     Length Name

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

-a---     2009. 11. 11.     21:41     105837 ActiveDirectoryRecycleBin.po

                                             werpack

-a---     2009. 11. 21.     19:45        254 futók.txt

-a---     2009. 11. 18.     17:46         40 script.ps1

-a---     2009. 11. 17.     19:49          7 szöveg.txt

Ezután létrehoztam egy háttérben futó folyamatot, amiben a Get-ChildItem cmdlettel keresem a keresendő fájlt. A scriptblock részben átadtam szándékaim szerint a $keresendőfájl változót, ennek ellenére, a [6]-os sorban lekérdezett eredményben a „munka” könyvtár össze fájlja szerepelt, nem csak a keresett. Azaz a globális scope-ban létrehozott változóm nem látszott a job számára.

Szerencsére lehet azért paramétert átadni, ezt pedig az –inputobject paraméter használatával tehetjük meg:

[7] PS C:\> $job = Start-Job -Name "Keres" -ScriptBlock {Get-ChildItem c:\

munka\$input -Recurse} -InputObject $keresendőfájl

[8] PS C:\> Receive-Job $job

 

 

    Directory: C:\munka

 

 

Mode                LastWriteTime     Length Name

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

-a---     2009. 11. 17.     19:49          7 szöveg.txt

Itt a [7]-es sorban az –inputobject paramétereként adom meg a $keresendőfájl változót, erre a szkriptblokkban a $input változóval tudok hivatkozni. És a [8]-as sor után látható eredményben már tényleg csak a keresett fájlt látjuk.

Az -InputObject paraméter tulajdonképpen a csővezetéket szimbolizálja, azaz a háttérfolymatom ezzel a csőből is betáplálható, akár az elemek egyesével feldolozhatók, ha van process blokkunk:

PS C:\> $job = 1,2,3,4 | Start-Job -ScriptBlock {process{$_ * 2}}

 

PS C:\> Get-Job

 

Id     Name            PSJobTypeName   State         HasMoreData     Location

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

2      Job2            BackgroundJob   Completed     True            localhost

 

PS C:\> Receive-Job -Job $job

2

4

6

8

A másik módja az adat átadásának az -ArgumentList paraméter használata:

PS C:\> $job = Start-Job -ScriptBlock {$args[0] * 2} -ArgumentList Valami

PS C:\> Receive-Job -Job $job

ValamiValami

Az -ArgumentList paraméter meg a hagyományos paraméterátadást szimbolizálja, ami a job oldalán az $args tömbben jelenik meg. Ha több adatot is át akarunk adni, akkor viszonylag kényelmetlen az $args tömb indexeivel játszadozni és elég könnyű „félreindexelni”. Szetencsére használhatunk nevesített paramétereket is:

PS C:\> $job = Start-Job -ScriptBlock {param($egyik, $másik) "$egyik $másik"} -

ArgumentList "eleje", "vége"

PS C:\> Receive-Job -Job $job

eleje vége

Ugyan itt már nem kellett a háttérben futó szkriptblokkban indexekkel bajlódni, de a Start-Job-nál még mindig könnyen hibázhatunk, ha rossz sorrendben adjuk meg az adatokat az –ArgumentList-nél. Ennél még könnyebb a helyzetünk PowerShell 3.0-tól, hiszen ott lehetőségünk van a using:  előtaggal még ezt is könnyebbé tenni:

PS C:\> $egyik = "eleje"

PS C:\> $másik = "vége"

PS C:\> $job = Start-Job -ScriptBlock {"$using:egyik $using:másik"}

PS C:\> Receive-Job -Job $job

eleje vége

Látható, hogy itt el is hagytam bármilyen paraméter használatát, a using: implicit módon adja át a paraméterértékeket a háttérben futó folyamatnak, ha azok már valamilyen változóban vannak.

Amit fontos tudni, mind az adatok átadásról és az eredmények átvételéről, hogy szerializáláson mennek keresztül az adatok, azaz olyan, mintha Export-CLIXML és Import-CLIXML cmdletekkel adtuk volna át az adatokat:

PS C:\> $p = Get-Process -Id $pid

PS C:\> $job = Start-Job -ScriptBlock {$using:p | gm}

PS C:\> Receive-Job -Job $job

 

 

   TypeName: Deserialized.System.Diagnostics.Process

 

Name                       MemberType   Definition

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

GetType                    Method       type GetType()

ToString                   Method       string ToString(), string ToString(...

Handles                    NoteProperty int Handles=640

Name                       NoteProperty string Name=powershell

 

PS C:\> $job = Start-Job -ScriptBlock {Get-Service -Name wuauserv}

PS C:\> Receive-Job -Job $job | gm

 

 

   TypeName: Deserialized.System.ServiceProcess.ServiceController

 

Name                MemberType   Definition

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

GetType             Method       type GetType()

ToString            Method       string ToString(), string ToString(string ...

Name                NoteProperty string Name=wuauserv

PSComputerName      NoteProperty string PSComputerName=localhost

Látható, hogy mind az átküldött processz adat, mind az átvett szolgáltatás Deserialized objektum lett, azaz elvesztették az eredeti metódusaikat.



Word To HTML Converter