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:
60 . á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 újra definiá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.