Keresés a tulajdonságok között (Search-Property)

Gyakran találkozunk olyan objektumokkal, amelyek nagyon sok tulajdonsággal rendelkeznek. Ilyenek például a WMI objektumok vagy az ActiveDirectory-ban található objektumok. A sok tulajdonság között nehéz észrevenni, hogy például melyekben található meg a számítógépünk neve vagy melyekben találhatók e-mailcím jellegű adatok. Készítsünk tehát egy olyan függvényt, ami segít nekünk a tulajdonságok közti keresésben!

Első próbálkozásként ezt raktam össze:

function Search-Property1 {

param(

    [string] $pattern = ".",

    [Parameter(ValueFromPipeline = $true)] $object

)

process{

    $object.psobject.properties |

        Where-Object {$_.name -match $pattern -or $_.value -match $pattern} |

            Select-Object -Property @{n="Object"; e={$object.tostring()}}, Name, Value

}

}

Két paraméterem van: $pattern az a minta, amit keresünk és a $object amiben keresünk. A $pattern alaphelyzet szerinti értéke „.”, ami azt jelenti, hogy csak azokat a tulajdonságokat kapom meg, amelyekben legalább 1 bármilyen karakter van. Az $object érkezhet a csövön keresztül is, így a függvényen belül a process blokkot kell alkalmazni.

Itt az $object tulajdonságain keresztül ($object.psobject.properties) egy Where-Object-tel szűröm, hogy melyek azok a tulajdonságok, ahol vagy a tulajdonság neve vagy az értéke illeszkedik a megadott mintára. Ha találok ilyet, akkor abból csinálok egy egyedi objektumot, ami első oszlopként tartalmazza magának az objektumnak a szöveggé konvertált változatát, majd a tulajdonság nevét és végül a tulajdonság értékét:

PS C:\> get-date | Search-Property1 -pattern Date

 

Object                 Name                                     Value

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

2023. 01. 21. 13:44:45 DisplayHint                           DateTime

2023. 01. 21. 13:44:45 DateTime    2023. január 21., szombat 13:44:45

2023. 01. 21. 13:44:45 Date                     2023. 01. 21. 0:00:00

A fenti példában a Date mintára kerestem a get-date által kibocsátott objektumban. Látszik, hogy mind a tulajdonságok nevei között, mind az értékei között megtalálta a mintát.

Gyorsan bővítsük a függvényünket 3 kapcsoló paraméterrel!

function Search-Property2 {

param(

    [string] $pattern = ".",

    [Parameter(ValueFromPipeline = $true)] $object,

    [switch] $SearchInPropertyNames,

    [switch] $NotSearchInValues,

    [switch] $Literal

)

begin{

    if($Literal){

        $pattern = [regex]::Escape($pattern)

    }

}

process{

    $object.psobject.properties |

        Where-Object {($SearchInPropertyNames -and $_.name -match $pattern) -or (!$NotSearchInValues -and $_.value -match $pattern)} |

            Select-Object -Property @{n="Object"; e={$object.tostring()}}, Name, Value

}

}

A három kapcsoló közül az utolsó a $Literal. Ha ezt használjuk, akkor a begin blokkban átalakítom a mintát annak eszképelt változatára, azaz szó szerinti kereséseket tudok végrehajtani, nem pedig regex minta alapján. Az új változatban alapban a tulajdonságok neveiben nem keresünk, csak ha a $SearchInPropertyNames kapcsolót használjuk. Ha viszont pont csak a nevekben akarunk keresni, akkor a $NotSearchInValues kapcsolót használjuk. Ezeket a kapcsolókat integráltam a Where-Object feltételrendszerébe.

Az alábbi példában a szó szerinti „pöttyre” keresve alapban minden tulajdonságot megkapnék, viszont a ‑Literal kapcsolóval ezt kapom:

PS C:\> Get-Date | Search-Property2 -pattern "." -Literal

 

Object                 Name      Value                            

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

2023. 01. 21. 18:11:13 DateTime  2023. január 21., szombat 18:11:13

2023. 01. 21. 18:11:13 TimeOfDay 18:11:13.0798403                 

Ha a „Date”-re keresünk, akkor más-más eredményt kapunk annak függvényében, hogy a és kapcsolókat használjuk:

PS C:\> Get-Date | Search-Property2 -pattern Date

 

Object                 Name           Value

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

2023. 01. 21. 18:20:16 DisplayHint DateTime

 

 

PS C:\> Get-Date | Search-Property2 -pattern Date -SearchInPropertyNames

 

Object                 Name                                     Value

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

2023. 01. 21. 18:20:20 DisplayHint                           DateTime

2023. 01. 21. 18:20:20 DateTime    2023. január 21., szombat 18:20:20

2023. 01. 21. 18:20:20 Date                     2023. 01. 21. 0:00:00

 

 

PS C:\> Get-Date | Search-Property2 -pattern Date -SearchInPropertyNames -NotSe

archInValues

 

Object                 Name     Value

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

2023. 01. 21. 18:20:22 DateTime 2023. január 21., szombat 18:20:22

2023. 01. 21. 18:20:22 Date     2023. 01. 21. 0:00:00

Mivel lehetne még fejleszteni a függvényt? Például lehetne szűrni, hogy mely tulajdonságokban akarunk eleve keresni. Ezt két újabb paraméterrel tudjuk megadni, a $Property és az $ExcludeProperty szövegtömbökkel:

function Search-Property3 {

param(

    [string] $pattern = ".",

    [Parameter(ValueFromPipeline = $true)] $object,

    [switch] $SearchInPropertyNames,

    [switch] $NotSearchInValues,

    [switch] $Literal,

    [string[]] $Property = "*",

    [string[]] $ExcludeProperty

)

begin{

    if($Literal){

        $pattern = [regex]::Escape($pattern)

    }

}

process{

    $object.psobject.properties |

        Where-Object {

            $propname = $_.Name

            ($Property | Where-Object {$propname -like $_}) -and

            !($ExcludeProperty | Where-Object {$propname -like $_}) -and

            (($SearchInPropertyNames -and $_.name -match $pattern) -or (!$NotSearchInValues -and $_.value -match $pattern))

        } | Select-Object -Property @{n="Object"; e={$object.tostring()}}, Name, Value

}

}

Ezekkel a Where-Object feltételrendszerét kell kibővíteni. Mivel mindkét paraméter tömb, így két beágyazott Where-Object kifejezéssel kell vizsgálni, hogy az adott tulajdonságnév szerepel-e a kért tulajdonságok között ($Property) és nem szerepel a nem kértek között ($ExcludeProperty). Mivel kavarodás lenne a $_ használatából a kétszintű csővezetékeknél, így az aktuális tulajdonság nevét külön elmentem a $propname változóba. Mindkét paraméter esetében a -like operátorral vizsgálom az egyezést, így az ott használatos „*” és „?wildcard karaktereket használhatjuk. Nézzük a példákat:

PS C:\> Get-Date | Search-Property3 -pattern 2023

 

Object                 Name     Value

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

2023. 01. 21. 18:52:11 DateTime 2023. január 21., szombat 18:52:11

2023. 01. 21. 18:52:11 Date     2023. 01. 21. 0:00:00

2023. 01. 21. 18:52:11 Year     2023

 

 

PS C:\> Get-Date | Search-Property3 -pattern 2023 -Property Date*

 

Object                 Name     Value

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

2023. 01. 21. 18:52:27 DateTime 2023. január 21., szombat 18:52:27

2023. 01. 21. 18:52:27 Date     2023. 01. 21. 0:00:00

 

 

PS C:\> Get-Date | Search-Property3 -pattern 2023 -Property Date* -ExcludePrope

rty *Time

 

Object                 Name Value

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

2023. 01. 21. 18:52:33 Date 2023. 01. 21. 0:00:00

Az első kifejezésben a „2023”-at tartalmazó tulajdonságokat kerestem, három találatot kaptam. A másodikban ezen belül csak a „Date”-tel kezdődőkre vagyok kíváncsi, itt már csak 2 találatom lett. Az utolsó kifejezésben a „Time” végűekre nem vagyok kíváncsi.

Mivel lehetne még gazdagítani a függvényt? Például azzal, hogy a mintánkban lehessen hivatkozni egy tulajdonság értékére. Például: keresem azokat a tulajdonságokat, amelyekben megtalálható az XY tulajdonságban tárolt érték. Nézzük példaként a Win32_ComputerSystem CIM objektumot:

PS C:\> Get-CimInstance -ClassName Cim_ComputerSystem | fl *

 

 

AdminPasswordStatus         : 0

BootupState                 : Normal boot

ChassisBootupState          : 3

KeyboardPasswordStatus      : 0

PowerOnPasswordStatus       : 0

PowerSupplyState            : 3

PowerState                  : 0

FrontPanelResetStatus       : 0

ThermalState                : 3

Status                      : OK

Name                        : STLENO

PowerManagementCapabilities :

PowerManagementSupported    :

Caption                     : STLENO

Description                 : AT/AT COMPATIBLE

Rengeteg tulajdonsága van, itt most csak az első néhányat másoltam ide. Látható, hogy a Name és Caption tulajdonság is tartalmazza a gépem nevét, de még több ilyen is van. Keresem az összes olyan tulajdonságot, ahol a Name tulajdonság értéke szerepel:

function Search-Property4 {

param(

    [string] $pattern = ".",

    [Parameter(ValueFromPipeline = $true)] $object,

    [switch] $SearchInPropertyNames,

    [switch] $NotSearchInValues,

    [switch] $Literal,

    [string[]] $Property = "*",

    [string[]] $ExcludeProperty

)

begin{

    if($Literal){

        $pattern = [regex]::Escape($pattern)

    }

    $origpattern = $pattern

}

process{

    if(!$LiteralSearch -and $origpattern  -match "<[^>]+>"){

        $pattern = [regex]::Replace($origpattern, "<([^>]+)>", {[regex]::Escape($object.($args[0].value -replace "<|>"))})

    }

   

    $object.psobject.properties |

        Where-Object {

            $propname = $_.Name

            ($Property | Where-Object {$propname -like $_}) -and

            !($ExcludeProperty | Where-Object {$propname -like $_}) -and

            (($SearchInPropertyNames -and $_.name -match $pattern) -or (!$NotSearchInValues -and $_.value -match $pattern))

        } | Select-Object -Property @{n="Object"; e={$object.tostring()}}, Name, Value

}

}

Ez a változat nem igényel újabb paramétert. Az eddigi $pattern paraméterben ha egy „<tulajdonságnév>” kifejezést szerepeltetek (kacsacsőrök között a tulajdonság neve), akkor azt a függvény behelyettesíti a „tulajdonságnév” alatti tulajdonság értékével. Mivel így a minta dinamikusan kell hogy változzon, ha több objektumot is betöltök a csőből, így el kell tároljam ez eredeti mintát a $origpattern változóba, ezt a begin részben teszem meg, majd egy jópofa regex kifejezéssel történik meg a dinamikus csere. Ezt láthattuk a „ 1.4.7.16 Csere .NET osztállyal ” fejezetben. Itt még annyi a trükk, hogy a dinamikus csere közben még egy [regex]::Escape metódust is meghívok.

Nézzük ezt használat közben:

PS C:\> Get-CimInstance -ClassName Cim_ComputerSystem | Search-Property4 -patte

rn "<Name>"

 

Object                                         Name                  Value

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

Win32_ComputerSystem: STLENO (Name = "STLENO") Caption               STLENO

Win32_ComputerSystem: STLENO (Name = "STLENO") Name                  STLENO

Win32_ComputerSystem: STLENO (Name = "STLENO") DNSHostName           STLeno

Win32_ComputerSystem: STLENO (Name = "STLENO") UserName              STLeno...

Win32_ComputerSystem: STLENO (Name = "STLENO") CimInstanceProperties {Capti...

 

 

PS C:\> Get-CimInstance -ClassName Cim_ComputerSystem | Search-Property4 -patte

rn "^<Name>$"

 

Object                                         Name        Value

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

Win32_ComputerSystem: STLENO (Name = "STLENO") Caption     STLENO

Win32_ComputerSystem: STLENO (Name = "STLENO") Name        STLENO

Win32_ComputerSystem: STLENO (Name = "STLENO") DNSHostName STLeno

Az első esetben a Name tulajdonság értékét keresem, a találatokban az is szerepel, ahol a gépem neve, mint rész-sztring szerepel. A második példában csak teljes egyezést keresek.

Az utolsó verzióban néhány esztétikai módosítást teszek még. Egyrészt képessé teszem a függvényt, hogy a több objektumot ne csak a csőből, hanem az $object paraméterbe tömbként is át lehessen adni. Ehhez a $object paraméter elé teszem a [PSObject[]] típusjelölőt, másrészt a processz blokkban egy foreach ciklussal megyek végig az elemeken és ennek megfelelően a cikluson belül a $o ciklusváltozóra hivatkozom:

function Search-Property {

param(

    [string] $pattern = ".",

    [Parameter(ValueFromPipeline = $true)] [PSObject[]] $object,

    [switch] $SearchInPropertyNames,

    [switch] $NotSearchInValues,

    [switch] $Literal,

    [string[]] $Property = "*",

    [string[]] $ExcludeProperty

)

begin{

    if($Literal){

        $pattern = [regex]::Escape($pattern)

    }

    $origpattern = $pattern

}

process{

    foreach($o in $object){

        if(!$LiteralSearch -and $origpattern  -match "<[^>]+>"){

            $pattern = [regex]::Replace($origpattern, "<([^>]+)>", {[regex]::Escape($o.($args[0].value -replace "<|>"))})

        }

   

        $o.psobject.properties |

            Where-Object {

                $propname = $_.Name

                ($Property | Where-Object {$propname -like $_}) -and

                !($ExcludeProperty | Where-Object {$propname -like $_}) -and

                (($SearchInPropertyNames -and $_.name -match $pattern) -or (!$NotSearchInValues -and $_.value -match $pattern))

            } | Select-Object -Property @{n="Object"; e={$o.tostring()}}, Name, Value

    }

}

}

Akkor erre is nézzünk néhány példát:

PS C:\> Get-ChildItem C:\PSTools\d* | Search-Property -pattern "<extension>"

 

Object                       Name        Value

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

C:\PSTools\data-20230102.csv PSPath      Microsoft.PowerShell.Core\FileSyst...

C:\PSTools\data-20230102.csv PSChildName data-20230102.csv

C:\PSTools\data-20230102.csv VersionInfo File:             C:\PSTools\data-...

C:\PSTools\data-20230102.csv Name        data-20230102.csv

C:\PSTools\data-20230102.csv FullName    C:\PSTools\data-20230102.csv

C:\PSTools\data-20230102.csv Extension   .csv

C:\PSTools\dummy.ps1         PSPath      Microsoft.PowerShell.Core\FileSyst...

C:\PSTools\dummy.ps1         PSChildName dummy.ps1

C:\PSTools\dummy.ps1         VersionInfo File:             C:\PSTools\dummy...

C:\PSTools\dummy.ps1         Name        dummy.ps1

C:\PSTools\dummy.ps1         FullName    C:\PSTools\dummy.ps1

C:\PSTools\dummy.ps1         Extension   .ps1

 

 

PS C:\> $files = Get-ChildItem C:\PSTools\d*

PS C:\> Search-Property -object $files -pattern "<extension>"

 

Object                       Name        Value

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

C:\PSTools\data-20230102.csv PSPath      Microsoft.PowerShell.Core\FileSyst...

C:\PSTools\data-20230102.csv PSChildName data-20230102.csv

C:\PSTools\data-20230102.csv VersionInfo File:             C:\PSTools\data-...

C:\PSTools\data-20230102.csv Name        data-20230102.csv

C:\PSTools\data-20230102.csv FullName    C:\PSTools\data-20230102.csv

C:\PSTools\data-20230102.csv Extension   .csv

C:\PSTools\dummy.ps1         PSPath      Microsoft.PowerShell.Core\FileSyst...

C:\PSTools\dummy.ps1         PSChildName dummy.ps1

C:\PSTools\dummy.ps1         VersionInfo File:             C:\PSTools\dummy...

C:\PSTools\dummy.ps1         Name        dummy.ps1

C:\PSTools\dummy.ps1         FullName    C:\PSTools\dummy.ps1

C:\PSTools\dummy.ps1         Extension   .ps1

Az első kifejezésben keresem a fájljaimnak azon tulajdonságait, ahol az extension tulajdonságuk értéke szerepel. Ezt itt a csövön keresztül hajtom végre. Az ezt követő példában ezeket a fájlokat előbb berakom a $files változóba, majd hagyományos módon adom át ezt a tömböt a $object paraméternek.



Word To HTML Converter