Az előző fejezetben tárgyalt Get-Property-hez hasonló a következő Expand-Property függvény. Itt nem egy konkrét elérési útra vagyok kíváncsi, hanem ki akarom fejteni egy objektum összes tulajdonságát vagy hashtábla összes kulcsát.
Itt a függvény definíciója:
function Expand-Property {
<#
.Synopsis
Expands all
the properties or keys of the input object.
.DESCRIPTION
Recursively
dumps all properties or keys of the input object. By default it goes 1 level
deep, but with the -MaxDepth parameter you can allow deep search.
If the
-Condensed switch is used, only the leaf properties are returned (properties
that don't have any further properties or which are at the -MaxDepth).
.EXAMPLE
Expand-Property -Object $PSVersionTable -MaxDepth 2 -SkipTypesAdditional
system.version
Expands the
properties of the $PSVersionTable object down to 2 level deep, but any
[system.version] type of property won't be expanded further.
.EXAMPLE
Expand-Property -Object $PSVersionTable -MaxDepth 2 -SkipTypesAdditional
system.version -Condensed
Expands only
the last properties in the hiararchy of properties in the $PSVersionTable
object down to 2 level deep, but any [system.version] type of property won't be
expanded further.
.INPUTS
hashtables or
psobjects
.OUTPUTS
Collection of
custom objects having a PropertyPath, Type, and Value properties.
#>
[cmdletbinding(PositionalBinding=$false)]
param(
# Input object or
hashtable
$Object,
# Maximum depth of
recursion, default is 1
[int]
$MaxDepth
=
1,
[Parameter(Dontshow
=
$true)]$Path,
[Parameter(Dontshow
=
$true)]$_currentDepth
=
1,
# Only leaf
properties / keys are returned
[switch]
$Condensed,
# .NET types that
are not expanded in properties / keys
[string[]]
$SkipTypesDefault
=
('System.Int*',
'System.UInt*',
'System.Double',
'System.Decimal',
'System.String',
'System.DateTime',
'System.TimeSpan',
'System.RuntimeType'),
[string[]]
$SkipTypesAdditional
)
if(!$Path){
$parts
=
[scriptblock]::Create($MyInvocation.Line).ast.findall({$true},$true)
for($i
=
0;
$i
-lt
$parts.count;
$i++){
if($parts[$i].ParameterName
-eq
'Object'){
$Path
=
$parts[$i
+
1].Extent.Text
if($path
-notmatch
'^(\$|\()'){
$Path =
"($Path)"
}
break
}
}
if(!$Path){
$Path
=
'$Object'
}
}
if($null -eq $Object
-or
$Object
-is
[System.DBNull]){
return
}
elseif($_currentDepth -gt
$MaxDepth
-or
($PSBoundParameters.ContainsKey('_currentDepth')
-and
($SkipTypesDefault
+
$SkipTypesAdditional
|
&{process{if($Object.GetType().fullname
-like
$_){$_}}}))){
[pscustomobject]
@{
PropertyPath = $Path
Type = $(if($null -ne
$Object){$Object.GetType().fullname})
Value = $Object
}
return
}
if(!$Condensed){
[pscustomobject]@{
PropertyPath = $Path
Type = $(if($null -ne
$Object){$Object.GetType().fullname})
Value = $Object
}
}
if($Object -is [hashtable]
-or
$Object
-is
[System.Collections.Specialized.OrderedDictionary]){
$keys
=
$Object.Keys
}
else{
$keys
=
$Object.psobject.properties.name
}
foreach($key in $keys){
$displayKey
=
$key
if($key
-match
'\W'){
$displayKey
=
"'$key'"
}
if($Object.$key
-is
[System.Collections.IList]){
if(!$Condensed){
[pscustomobject]@{
PropertyPath =
"$Path.$displayKey"
Type = $(if($null
-ne
$Object.$key){$Object.$key.GetType().fullname})
Value = $Object.$key
}
}
if($_currentDepth
-lt
$MaxDepth){
for($i
=
0;
$i
-lt
$Object.$key.count;
$i++){
Expand-Property
-Object
$Object.$key[$i]
-Path
($Path
+
"."
+
$displayKey
+
"[$i]")
-MaxDepth
$MaxDepth
-Condensed:$Condensed
-_currentDepth
($_currentDepth
+
2)
-SkipTypesDefault
$SkipTypesDefault
-SkipTypesAdditional
$SkipTypesAdditional
}
}
}
elseif($null
-eq
$Object.$key
-or
$Object.$key
-is
[System.DBNull]){
[pscustomobject]@{
PropertyPath = "$Path.$displayKey"
Type = $(if($null -ne
$Object.$key){$Object.$key.GetType().fullname})
Value = $Object.$key
}
}
else{
Expand-Property
-Object
$Object.$key
-Path
($Path
+
"."
+
$displayKey)
-MaxDepth
$MaxDepth
-Condensed:$Condensed
-_currentDepth
($_currentDepth
+
1)
-SkipTypesDefault
$SkipTypesDefault
-SkipTypesAdditional
$SkipTypesAdditional
}
}
}
Nézzünk pár példát a felhasználására! Elsőként vegyük elő újra azt a $Ticket hashtáblát, amit már korábban használtunk:
$ticket = @{
Number = "REQ1234"
Approver = @{
Name = "Tibor Soós"
EmployeeId = 99123
Manager = @{
Name = "Ferenc Főnök"
EmployeeID = 87871
}
}
items = @(
@{
Number = "RITM332211"
Description = "Valami jó"
ItemCost = @{
Price = 1234
Currency =
"HUF"
}
},
@{
Number = "RITM332212"
Description = "Ingyenes"
ItemCost = $null
}
)
}
Ha nincs meg a forráskódunk, de a $Ticket megvan és megnézzük azt, akkor nem annyira látjuk át, hogy mi is van benne:
PS C:\> $ticket
Name Value
---- -----
Number REQ1234
items {System.Collections.Hashtable, System.Colle...
Approver {EmployeeId, Name, Manager}
Itt jön jól az Expand-Property függvényünk:
PS C:\> Expand-Property -Object $ticket
PropertyPath Type Value
------------ ---- -----
$ticket System.Collections.Hashtable {Number, items, Approver}
$ticket.Number System.String REQ1234
$ticket.items System.Object[] {System.Collections.Hashtabl...
$ticket.Approver System.Collections.Hashtable {EmployeeId, Name, Manager}
Az első próbálkozás nem javít túl sokat, de engedjük a tulajdonságok / kulcsok bejárását nagyobb mélységben:
PS C:\> Expand-Property -Object $ticket -MaxDepth 100
PropertyPath Type Value
------------ ---- -----
$ticket System.Collections.Hashtable {Number, ...
$ticket.Number System.String REQ1234
$ticket.items System.Object[] {System.C...
$ticket.items[0] System.Collections.Hashtable {ItemCost...
$ticket.items[0].ItemCost System.Collections.Hashtable {Price, C...
$ticket.items[0].ItemCost.Price System.Int32 1234
$ticket.items[0].ItemCost.Currency System.String HUF
$ticket.items[0].Number System.String RITM332211
$ticket.items[0].Description System.String Valami jó
$ticket.items[1] System.Collections.Hashtable {ItemCost...
$ticket.items[1].ItemCost
$ticket.items[1].Number System.String RITM332212
$ticket.items[1].Description System.String Ingyenes
$ticket.Approver System.Collections.Hashtable {Employee...
$ticket.Approver.EmployeeId System.Int32 99123
$ticket.Approver.Name System.String Tibor Soós
$ticket.Approver.Manager System.Collections.Hashtable {Name, Em...
$ticket.Approver.Manager.Name System.String Ferenc Főnök
$ticket.Approver.Manager.EmployeeID System.Int32 87871
Látható, hogy a függvény bejárta a teljes hierarchiát és minden szinten kiírja, hogy mi az adott szint elérési útja (PropertyPath oszlop), mi az ottani adat típusa (Type) és értéke (Value). Ráadásul a PropertyPath alatti bármelyik elérési utat ki tudjuk másolni és végrehajtva megkapjuk azt az értéket, amit a Value oszlopban látunk:
PS C:\> $ticket.items[0].Description
Valami jó
A fenti lista némileg redundáns információt tartalmaz, hiszen például nem kellene nekünk feltétlenül a legelső találat ($ticket), vagy a $ticket.items, hiszen az ezek alatti, legvégső tulajdonságok / kulcsok alapján is nagyjából felépíthető lenne az eredeti objektum. Ha ezekre a redundáns infókra nincs szükségünk, akkor kihagyhatjuk őket a -Condensed kapcsoló használatával:
PS C:\> Expand-Property -Object $ticket -MaxDepth 100 -Condensed
PropertyPath Type Value
------------ ---- -----
$ticket.Number System.String REQ1234
$ticket.items[0].ItemCost.Price System.Int32 1234
$ticket.items[0].ItemCost.Currency System.String HUF
$ticket.items[0].Number System.String RITM332211
$ticket.items[0].Description System.String Valami jó
$ticket.items[1].ItemCost
$ticket.items[1].Number System.String RITM332212
$ticket.items[1].Description System.String Ingyenes
$ticket.Approver.EmployeeId System.Int32 99123
$ticket.Approver.Name System.String Tibor Soós
$ticket.Approver.Manager.Name System.String Ferenc Főnök
$ticket.Approver.Manager.EmployeeID System.Int32 87871
Itt már csak a legszükségesebb sorokat kaptuk meg.
Nézzünk még egy példát! Itt a $PSVersionTable automatikus változót vizsgálom:
PS C:\> Expand-Property -Object $PSVersionTable -MaxDepth 100
PropertyPath Type
------------ ----
$PSVersionTable System.Collections...
$PSVersionTable.PSVersion System.Version
$PSVersionTable.PSVersion.Major System.Int32
$PSVersionTable.PSVersion.Minor System.Int32
$PSVersionTable.PSVersion.Build System.Int32
$PSVersionTable.PSVersion.Revision System.Int32
$PSVersionTable.PSVersion.MajorRevision System.Int16
$PSVersionTable.PSVersion.MinorRevision System.Int16
$PSVersionTable.PSEdition System.String
$PSVersionTable.PSCompatibleVersions System.Version[]
$PSVersionTable.PSCompatibleVersions[0] System.Version
$PSVersionTable.PSCompatibleVersions[0].Major System.Int32
$PSVersionTable.PSCompatibleVersions[0].Minor System.Int32
$PSVersionTable.PSCompatibleVersions[0].Build System.Int32
$PSVersionTable.PSCompatibleVersions[0].Revision System.Int32
$PSVersionTable.PSCompatibleVersions[0].MajorRevision System.Int16
$PSVersionTable.PSCompatibleVersions[0].MinorRevision System.Int16
…
Megvágtam a kimenetet, mert a nagyon sok verziószám típusú tulajdonság, ami itt megtalálható, nagyon hosszúvá teszi ezt a listát, hiszen minden verziószámnak van külön Major, Minor, Build, Revision, stb. tulajdonsága. Ha ezeket nem akarom látni, akkor kikapcsolhatom ezen típus kifejtését a -SkipTypeAdditional paraméter használatával:
PS C:\> Expand-Property -Object $PSVersionTable -MaxDepth 100 -SkipTypesAdditional 'system.version'
PropertyPath Type Value
------------ ---- -----
$PSVersionTable System.Collections.Hashtable {PS...
$PSVersionTable.PSVersion System.Version 5.1...
$PSVersionTable.PSEdition System.String Des...
$PSVersionTable.PSCompatibleVersions System.Version[] {1....
$PSVersionTable.PSCompatibleVersions[0] System.Version 1.0
$PSVersionTable.PSCompatibleVersions[1] System.Version 2.0
$PSVersionTable.PSCompatibleVersions[2] System.Version 3.0
$PSVersionTable.PSCompatibleVersions[3] System.Version 4.0
$PSVersionTable.PSCompatibleVersions[4] System.Version 5.0
$PSVersionTable.PSCompatibleVersions[5] System.Version 5.1...
$PSVersionTable.BuildVersion System.Version 10....
$PSVersionTable.CLRVersion System.Version 4.0...
$PSVersionTable.WSManStackVersion System.Version 3.0
$PSVersionTable.PSRemotingProtocolVersion System.Version 2.3
$PSVersionTable.SerializationVersion System.Version 1.1...
Így már jóval rövidebb lett a lista. A függvénydefinícióban látható, hogy a -SkipTypesDefault alatt már jónéhány olyan típus van, amit nem fejt ki a függvény, ezt lehet bővíteni a -SkipTypeAdditional-lal. Persze a default listát is felül lehet bírálni, de én azt nem javaslom.
Amúgy annak ellenére, hogy például a [system.datetime] típus benne van az alap listában, mégis kifejtésre kerül, ha az nem egy objektum tulajdonságában van, hanem direktben adjuk input objektumként:
PS C:\> Expand-Property -Object (get-date)
PropertyPath Type Value
------------ ---- -----
(get-date) System.DateTime 2025....
(get-date).DisplayHint Microsoft.PowerShell.Commands.DisplayHintType DateTime
(get-date).DateTime System.String 2025....
(get-date).Date System.DateTime 2025....
(get-date).Day System.Int32 29
(get-date).DayOfWeek System.DayOfWeek Tuesday
(get-date).DayOfYear System.Int32 210
(get-date).Hour System.Int32 17
(get-date).Kind System.DateTimeKind Local
(get-date).Millisecond System.Int32 45
(get-date).Minute System.Int32 27
(get-date).Month System.Int32 7
(get-date).Second System.Int32 44
(get-date).Ticks System.Int64 63889...
(get-date).TimeOfDay System.TimeSpan 17:27...
(get-date).Year System.Int32 2025