Objektumok testre szabása, kiegészítése (Add-Member)

Elöljáróban annyit meg kell jegyezni, hogy természetesen a PowerShell parancssori értelmezőjétől nem várhatjuk el, hogy magát a .NET osztályokat módosítsa. A PowerShell csak a saját „generic” osztályát, a PSObject  osztályt és annak példányait képes módosítani.

Vegyünk egy általános objektumot, hívjuk őt „Base Object”-nek. A PowerShell elképzelhető, hogy nem minden metódust és tulajdonságot tesz elérhetővé, ezeket egy adapter réteggel elfedi. Ha bármilyen módosítást, kiegészítést teszünk ehhez az objektumhoz, például definiálunk mi magunk valamilyen tulajdonságot vagy metódust hozzá, akkor azt egy újabb, „Extended Types” rétegben tesszük meg. Így alakul ki a végleges tulajdonság- és metóduslistánk, amit az alábbi ábra illusztrál:

55 . ábra PowerShell adaptált objektummodellje

Az így elérhető objektum kettős jelleggel bír: egyrészt hordozza magában az eredeti „Base Object” jellegét, de valójában ez már egy új, PSObject típusú objektum lesz.

Ennek megértéséhez nézzük meg az alábbi példát, melyben az Add-Member  cmdlettel bővítek ki egy objektumot újabb tulajdonsággal, vagy egyéb tagjellemzővel. Ilyen tagjellemzők lehetnek:

Tagjellemző típus

Leírás

AliasProperty

Álnév egy már meglevő tulajdonságra.

All

Minden lehetséges taglehetőség típus.

CodeMethod

Olyan metódus, amellyel hivatkozunk egy .NET osztály statikus metódusára.

CodeProperty

Olyan tulajdonság, amellyel hivatkozunk egy .NET osztály statikus tulajdonságára.

MemberSet

Tulajdonságok és metódusok halmaza egy közös néven.

Method

A PSObject alapjaként szolgáló objektumosztály egyik metódusa.

Methods

Minden metódus.

NoteProperty

Tulajdonságnév és tulajdonságérték páros, nem számított, hanem fix tag.

ParameterizedProperty

Olyan tulajdonság, ami paraméterezhető.

Properties

Minden tulajdonság.

Property

A PSObject alapjaként szolgáló objektumosztály egyik tulajdonsága.

PropertySet

Tulajdonsághalmaz.

ScriptMethod

Szkripttel megfogalmazott metódus.

ScriptProperty

Szkripttel megfogalmazott tulajdonság.

Ezek közül leggyakrabban NoteProperty, ScriptProperty, ScriptMethod testre szabási lehetőséget alkalmazzuk.

Akkor nézzük a példát! Vegyük a PowerShell éppen futó processz objektumát:

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

PS C:\> $p

 

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

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

    696      29    58708      70280 ...95     2,30  11636   2 powershell

Ennek van egy StartInfo tulajdonsága is, ami mutatja, hogy mikor indult el ez a processz:

PS C:\> $p.StartInfo

 

2016. május 2. 22:21:39

Erre alapozottan létrehozhatunk egy új HányPerceFut tulajdonságot is, ami az azóta eltelt időt mutatja percekben. Elsőként ezt NoteProperty -ként hozom létre. A NoteProperty jellegzetessége hogy mindent elő kell készíteni számára, a végeredményt adjuk át neki, ami ezek után egy statikus értékként az objektum teljes életében ott lesz:

PS C:\> Add-Member -InputObject $p -MemberType NoteProperty -Name HányPerceFu

t -Value ((get-date)-$p.StartTime).totalminutes

PS C:\> $p.HányPerceFut

7,57521812

PS C:\> $p.HányPerceFut

7,57521812

Látható fent, hogy ha egymás után többször lekérdezzük ezt az új tulajdonságot, akkor ugyanazt az értéket fogja tartalmazni, hiába telik azóta az idő.

Kicsit ízlelgessük a fenti Add-Member  kifejezést! Nem ad semmi kimenetet, hanem magán az ‑InputObject paraméterként átadott objektumba dolgozik bele. Lehet ezt másképp is kérni tőle, de ezt egy kicsit később.

Hozzuk létre ugyanezt ScriptProperty -ként! Újra a kezdetektől indulok a $p újradefiniálásával:

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

PS C:\> Add-Member -InputObject $p -MemberType ScriptProperty -Name HányPerce

Fut -Value {((get-date)-$this.StartTime).totalminutes}

Itt a $p-nek nem mi számoljuk ki a HányPerceFut tulajdonságát, hanem azt a szkriptet adjuk át, amivel ő ezt az értéket sajét magának ki tudja számolni minden esetben, amikor valaki lekéri azt. Magára az objektumra ebben a szkriptben a $this változóval tudunk hivatkozni.

A végeredmény annyival más, hogy ez a tulajdonság dinamikus, azaz az idő múlásával a HányPerceFut értéke változik:

PS C:\> $p.HányPerceFut

10,531181675

PS C:\> $p.HányPerceFut

10,58061289

Létrehozhatunk AliasProperty -t is, ami egy alternatív név valamely másik tulajdonságra:

PS C:\> Add-Member -InputObject $p -MemberType AliasProperty -Name RunTimeInM

inutes -Value HányPerceFut

PS C:\> $p.RunTimeInMinutes

12,8205495816667

A fenti példámban a HányPerceFut-ra hoztam létre altarnatívaként a RunTimeInMinutes nevet, ezek után mindkét néven ugyanazt a tulajdonságot érem el.

Összefoglalásul: a tulajdonságok minden esetben „kiszámolódnak”, amikor azokhoz hozzáférünk. Ez nagyon egyszerű, gyors művelet, ha a tulajdonság értéke statikus, viszont ha dinamikus, például egy bonyolultabb szkripttulajdonság, akkor ez hosszadalmasabb is lehet. Például, ha az lekérdezem a processz objektum „h” betűvel kezdődő tulajdonságait, ami között éppen ott van  a HányPerceFut is, akkor természetesen az kiszámolásra kerül:

PS C:\> $p | fl h*

 

 

HányPerceFut : 15,5288835683333

Handles      : 624

HasExited    : False

Handle       : 2760

HandleCount  : 624

Ha óvatosabbak akarunk lenni és csak nagyon kontrollált módon akarunk ehhez az adathoz hozzáférni, akkor érdemesebb lehet ezt inkább metódussal generálni. A következőben egy ScriptMethod -ot hozok létre, hasonló tartalommal, mint a ScriptProperty volt:

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

PS C:\> Add-Member -InputObject $p -MemberType ScriptMethod -Name HányPerceFu

t -Value {((get-date)-$this.StartTime).totalminutes}

Ilyenkor hiába kérdezek le akármennyi tulajdonságot, természetesen a metódus nem kerül meghívásra, így nem is kerül időbe se:

PS C:\> $p | fl h*

 

 

Handles     : 588

HasExited   : False

Handle      : 2644

HandleCount : 588

Ha viszont szeretnénk tudni, hogy hány perce fut a processz, akkor csak meghívjuk a metódust:

PS C:\> $p.HányPerceFut()

17,296448

A metódusoknak lehet paraméterük is, ami szintén részt vehet az eredmény képzésében. Például az alábbi MiótaFut metódus paramétereként megadhatjuk, hogy percekben, órákban vagy másodpercekben kérjük az eltelt időt:

PS C:\> Add-Member -InputObject $p -MemberType ScriptMethod -Name MiótaFut -V

alue {param($mérték)((Get-Date)-$this.starttime).("total$mérték")}

PS C:\> $p.MiótaFut("minutes")

13,5369275866667

PS C:\> $p.MiótaFut("hours")

0,2269305415

PS C:\> $p.MiótaFut("seconds")

822,6534283

Az Add-Member-el létrehozott új tagjellemzők az adott objektumhoz kötődik. Ha újra létrehozom az objektumot, akkor annak nem lesznek ilyen új tagjellemzői. Ha automatikusan szeretném minden új objektumnál elérhetővé tenni az új tagjellemzőimet, akkor magának az osztálynak (típusnak) a testre szabása lesz szükséges, amit a következő fejezetben mutatok be.

Az Add-Member cmdlettel azonban lehet egy trükköt elkövetni. A következő példában az automatikus változókhoz hasonló viselkedésű, azaz az aktuális értékét magától felvevő változót készítek, ami a gépem aktuális „fennléti idejét”, azaz az utolsó bebootolás óta eltelt időt mutatja:

$uptime = New-Object -TypeName PSObject

 

add-member -InputObject $uptime -MemberType scriptmethod -Value {

    $o = Get-WmiObject -Class WIn32_operatingsystem

    ((get-date) - $o.ConvertToDateTime($o.LastBootUpTime)).totaldays

} -name ToString -force

Ha ezt az $uptime változót létrehoztam, akkor az értéke mindig az aktuális fennléti időt mutatja, hiszen egy változó értékének kiírási folyamatának végén valójában annak a ToString metódusának meghívása történik, pont az, amit én az Add-Member-rel definiáltam.

PS C:\> $uptime

2,01081240596759

PS C:\> $uptime

2,01084795198264

Van még egy lehetőség az Add-Member-rel. Van egy SecondValue  paramétere is, amit ScriptProperty-k létrehozásakor használhatunk. Ez a SecondValue tulajdonképpen a tulajdonság Set metódusa. Nézzünk erre egy példát!

$a = New-Object -TypeName PSObject -Property @{érték = 0}

$a | Add-Member -MemberType ScriptProperty -Name értéknövelő -Value {$this.érték} -SecondValue {$this.érték += $args[0]}

 A példában az $a objektumomnak két tulajdonságot hoztam létre. Az „érték” egy egyszerű noteproperty, az „értéknövelő” egy scriptproperty, amelyben a Get metódus egyszerűen visszaadja az „érték” tulajdonságot, a SecondValue-ként megadott Set metódus azonban az „érték”-et megnöveli az értékadás jobb oldalán álló számmal, valahogy így:

PS C:\> $a

 

                                  érték                            értéknövelő

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

                                      0                                      0

 

 

PS C:\> $a.érték = 10

PS C:\> $a

 

                                  érték                            értéknövelő

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

                                     10                                     10

 

 

PS C:\> $a.értéknövelő = 5

PS C:\> $a

 

                                  érték                            értéknövelő

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

                                     15                                     15

Látható, hogy az „érték” és az „értéknövelő” kiolvasáskor ugyanazt tartalmazza. Ha az „érték”-nek adunk értéket, akkor azt minden trükk nélkül felveszi. Ellenben ha az „értéknövelő”-nek adok értéket, az valójában az „érték” növelésére lesz felhasználva.

Megjegyzés

Sajnos ezzel a módszerrel vigyázni kell. Nem lehet magára a scriptproperty-re hivatkozni az értékadásban, mert a PowerShell „elszáll”. Példa:

Itt egy „érték” tulajdonságot használtam és ennek hoztam létre külön Get és Set metódust. Már a Get sem működik, mert egy körkörös hivatkozás alakul ki, hiszen a $this.érték is magát a Get metódust hívja meg. Megoldás tehát az, hogy vagy egy másik tulajdonságra hivatkozunk, vagy egy globális változóra.



Word To HTML Converter