Gyűjtemények összehasonlítása (Compare-Object)

A Compare-Object  cmdlet segítségével két tetszőleges gyűjteményt hasonlíthatunk össze, kimenetül a gyűjtemények közötti különbséget leíró objektumokat kapunk. Az alábbi példában egy változóba mentettem a gépen futó folyamatok listáját. Ezután leállítottam, illetve elindítottam néhány folyamatot, majd ezt az új állapotot egy másik változóba írtam. A Compare-Object-nek odaadtam a két változót, ő pedig kilistázta a különbségeket:

[44] PS C:\> $a = Get-Process # bezár notepad, xmlnotepad

[45] PS C:\> $b = Get-Process # megnyit másik notepad példány, mspaint

[46] PS C:\> Compare-Object $a $b

 

InputObject                          SideIndicator

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

System.Diagnostics.Process (mspaint) =>

System.Diagnostics.Process (XmlNo... <=

A [44]-es sor futtatásakor a Notepad egy példánya futott, meg az XMLnotepad program. A [45]-ös sor futtatása előtt becsuktam a Notepadet és újra megnyitottam (másik processz lett belőle), és becsuktam az XMLnotepad-et és megnyitottam az MSPaint-et. A [46]-os sorban összehasonlítottam a két állapotban mintavételezett processzek listáját. Valamilyen szempontból jó eredményt kaptunk, de ha igazán belegondolunk, és precízek szerettünk volna lenni, akkor ez mégsem jó eredmény, hiszen a két notepad.exe folyamat az nem ugyanaz. Vajon hogyan gondolkodott a PowerShell? Szegény compare-object bármilyen bonyolult objektumok gyűjteményét kaphatja paraméterként, így ha az objektumok összes tulajdonságának összehasonlításával döntené el az egyezőséget, akkor nagyon sokat kellene dolgoznia. Így alaphelyzetben nagyon egyszerű algoritmust alkalmaz: veszi az objektumok szöveggé alakított formáját. A ToString  metódus minden objektumnál kötelező elem, így ez garantáltan meghívható. Nézzük meg, hogy ez mit az a fenti esetben:

[47] PS C:\> $a | ForEach-Object {$_.tostring()}

System.Diagnostics.Process (conhost)

System.Diagnostics.Process (csrss)

System.Diagnostics.Process (csrss)

System.Diagnostics.Process (dfsrs)

System.Diagnostics.Process (dfssvc)

Hiszen pont ilyen adatokat láthatunk a [46]-os sor futtatása után, és ebben tényleg csak a processz objektumok típusa és neve látszik, azaz elrejtődik, ha egy processzt újra nyitunk. Hogyan lehetne precízebbé tenni a compare-object-et? Használjuk a –property paramétert, ahol felsorolhatjuk, hogy pontosan mely tulajdonság(ok) összehasonlításán alapuljon az egyes objektumok egyformaságának eldöntése. A mi esetünkben legyen a processz azonosító és a processz neve:

[48] PS C:\> Compare-Object $a $b -Property id, name

 

                      id name                     SideIndicator

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

                    2076 mspaint                  =>

                    1632 notepad                  =>

                    2944 notepad                  <=

                     396 XmlNotepad               <=

Ez már precízebb eredményt adott!

Mire használható ez még? Szeretnénk például megtudni, hogy az internetről gyűjtött csodaprogram telepítője pontosan mit garázdálkodik a gépünkön? Semmi gond, készítsünk pillanatfelvételt az érzékeny területekről (futó folyamatok, fájlrendszer, registry) a telepítés előtt, majd hasonlítsuk össze a telepítőprogram lefutása utáni állapottal. Lesz nagy meglepetés! Nem kell elaprózni, bátran lekérhetjük például a teljes c: meghajtó állapotát, a gép majd beleizzad kicsit az összehasonlításba, de így mindenre fény derül:

PS C:\> $a = Get-ChildItem c: -recurse

PS C:\> $b = Get-ChildItem c: -recurse

PS C:\> Compare-Object $a $b

Vigyázzunk azonban a compare-object-tel! Ha túl sok a különbség a két gyűjtemény között, akkor elég sokáig eltarthat az összehasonlítgatás, hiszen az első kupac minden eleméhez megnézi, hogy van-e egyező elem a másik kupacban. Ha mindkét kupac közel azonos számú elemből áll és az elemek sorrendben vannak, akkor használhatjuk –SyncWindow paramétert, mellyel leszűkíthetjük azt a tartományt, amin belül egyezést keres a másik kupacban. Nézzünk erre egy példát:

[56] PS C:\> Compare-Object 1,2,3 3,4,5 -SyncWindow 1

 

                         InputObject SideIndicator

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

                                   3 =>

                                   1 <=

                                   4 =>

                                   2 <=

                                   5 =>

                                   3 <=

A fenti példában az egyik gyűjteményem 1-től 3-ig a számok, a másik gyűjteményem 3-tól 5-ig. Azaz a 3 valójában nem különbség a két gyűjtemény között, mégis az eredményben, ami ugye az eltéréseket adja meg, a 3-as is szerepel, ráadásul kétszer is. Ennek az az oka, hogy a compare‑object 1-es synwindow paraméterrel nem minden elemhez néz meg minden elemet, hanem alaphelyzetben ±1 elem távolságra. Azaz az első halmaz 3-asát összehasonlítja a második tömbbeli 4-gyel, 5-tel, de az ottani első 3-assal már nem. A compare-object alaphelyzetben ±maxint távolságra vizsgál, azaz gyakorlatilag minden elemhez minden elemet megkeres:

[57] PS C:\> Compare-Object 1,2,3 3,4,5

 

                         InputObject SideIndicator

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

                                   4 =>

                                   5 =>

                                   1 <=

                                   2 <=

Így megtalálta, hogy mindkét halmazban benne van a 3-as.

A compare-object akár több tulajdonság együttállását is képes összehasonlítani:

PS C:\> $a = Get-Process

PS C:\> $b = Get-Process

PS C:\> Compare-Object $a $b -Property id, workingset

 

                        id                workingset SideIndicator

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

                      3564                  59248640 =>

                       348                  62193664 =>

                      2904                   6524928 =>

                      3564                  61214720 <=

                       348                  62160896 <=

                      2904                   6508544 <=

A fenti példában 1-2 másodperces eltéréssel vettem mintát a futó processzeimből, ha csak ID-re vizsgáltam volna, akkor nem lett volna különbség a két kupac között, de így, hogy ID-re és workingset-re együtt vizsgálram, így már látható, hogy három processznek változott meg a memóriafelhasználása ilyen rövid idő alatt.

Ha tovább vizsgáljuk az így kapott kimenetet, akkor láthatjuk, hogy annak szerkezete eléggé eltávolodott az eredeti objaktumok típusától:

PS C:\> Compare-Object $a $b -Property id, workingset | gm

 

 

   TypeName: System.Management.Automation.PSCustomObject

 

Name          MemberType   Definition

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

Equals        Method       bool Equals(System.Object obj)

GetHashCode   Method       int GetHashCode()

GetType       Method       type GetType()

ToString      Method       string ToString()

id            NoteProperty System.Int32 id=3564

SideIndicator NoteProperty System.String SideIndicator==>

workingset    NoteProperty System.Int32 workingset=59248640

De vajon hogyan lehetne valahogy megőrizni a kimenetben is a processz objektumokat? Erre –PassThru paraméter ad megoldást:

PS C:\> Compare-Object $a $b -Property id, workingset -PassThru

 

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

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

    366      23    58216      57860   574     1,98   3564 powershell

   1493      77    49944      60736   507             348 svchost

     76       7     2864       6372    31            2904 unsecapp

    463      24    60192      59780   574     2,00   3564 powershell

   1481      77    49840      60704   506             348 svchost

     74       7     2836       6356    30            2904 unsecapp

Itt viszont nem látom a változás irányát! De szerencsére azért az ott van a mélyén:

PS C:\> Compare-Object $a $b -Property id, workingset -PassThru | ft id, proces

sname, workingset, sideindicator

 

                 Id ProcessName                  WorkingSet SideIndicator

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

               3564 powershell                     59248640 =>

                348 svchost                        62193664 =>

               2904 unsecapp                        6524928 =>

               3564 powershell                     61214720 <=

                348 svchost                        62160896 <=

               2904 unsecapp                        6508544 <=

Azaz ezzel a kapcsoval minden kimeneti objektum megőrizte ereteti mivoltát, csak egy plusz SideIndicator tulajdonságot kapott.



Word To HTML Converter