【问题标题】:Iterate over PSObject properties in PowerShell在 PowerShell 中迭代 PSObject 属性
【发布时间】:2021-11-10 21:25:30
【问题描述】:

我有这个PSObject(来自 XML):

bool : {IsActive, ShowOnB2C, ShowOnB2B, IsWebNews}
str  : {id, ProductId, GroupName, Unit...}
int  : {ProductIdNumeric, Prices_SalesOne, Prices_Treater, Prices_B2C...}
arr  : {EnvironmentBrands, Catalogs, NavisionLevels}
long : long

我想在不使用属性名称的情况下迭代属性,例如bool

我试图像这样索引对象:

$document[0]

但这没有给我任何东西,但它也不会导致任何错误。

Select-Object 有点工作,但我必须使用属性名称,我不希望那样。

$documents | Select-Object bool,str

ForEach 不要迭代属性。

$documents | ForEach {
    $_.name
}

返回 doc,这是包含 bool、int 和字符串的标记 (XML) 的名称。

【问题讨论】:

  • 你可以看看Get-Member cmdlet。

标签: powershell


【解决方案1】:

这可以使用隐藏属性PSObject

$documents.PSObject.Properties | ForEach-Object {
    $_.Name
    $_.Value
}

这不适用于某些 PowerShell 创建的包含“NoteProperties”(NoteProperty 类型的属性)的对象 (PSObjects)。

See this answer for a method that covers all property types.

【讨论】:

  • $documents.PSObject.Properties | ForEach-Object 迭代原始对象,尽管 $documents 是原始 XML 对象的子集$documents = $responseContent.ChildNodes.result.doc
  • 这不适用于具有 NoteProperty 成员而不是 Property 成员(如 cmdlet 的对象输出)的 PSCustomObjects。您必须使用下面另一个答案中显示的 get-member 语法来涵盖对象属性的所有变体。
  • 谢谢@Blaisem 我已经用另一个链接更新了这个答案。
【解决方案2】:

我更喜欢使用 foreach 循环 PowerShell 对象:

foreach($object_properties in $obj.PsObject.Properties)
{
    # Access the name of the property
    $object_properties.Name

    # Access the value of the property
    $object_properties.Value
}

一般情况下,foreach 的性能高于Foreach-Object

是的,foreach 实际上与底层的 Foreach-Object 不同。

【讨论】:

    【解决方案3】:

    您可能还需要使用 Get-Member 的 NoteProperty。

    $documents | Get-Member -membertype property,noteproperty | 
      Foreach name
    

    编辑:类型“属性”似乎更通用

    $documents | get-member -type properties | % name
    

    编辑:转储所有值:

    $obj = ls test.ps1
    $obj | Get-Member -Type properties | foreach name | 
      foreach { $_ + ' = ' + $obj.$_ }
    
    Attributes = Normal
    CreationTime = 06/01/2019 11:29:03
    CreationTimeUtc = 06/01/2019 15:29:03
    Directory = /Users/js
    DirectoryName = /Users/js
    Exists = True
    Extension = .ps1
    FullName = /Users/js/test.ps1
    IsReadOnly = False
    LastAccessTime = 06/05/2019 23:19:01
    LastAccessTimeUtc = 06/06/2019 03:19:01
    LastWriteTime = 06/01/2019 11:29:03
    LastWriteTimeUtc = 06/01/2019 15:29:03
    Length = 55
    Name = test.ps1
    

    没有“| foreach name”的另一种方式,需要额外的括号:

    $obj | Get-Member -Type properties | 
      foreach { $_.name + ' = ' + $obj.($_.name) }
    

    另一个结果是数组更易于使用,并且可能适用于从 json 转换的对象:

    $a = '{ prop1:1, prop2:2, prop3:3 }' | convertfrom-json     
    $a
    
    prop1 prop2 prop3
    ----- ----- -----
        1     2     3
    
    $a.PSObject.Properties | select name,value
    
    name  value
    ----  -----
    prop1     1
    prop2     2
    prop3     3
    

    【讨论】:

    • 这是一般的答案。 $object.psobject.properties 仅适用于从 cmdlet 输出的对象。 PSCustomObjects 使用 NoteProperties 并且需要 | gm -MemberType NoteProperty 语法,或者 -MemberType properties 这将适用于具有属性或 noteproperties 的对象。
    【解决方案4】:

    stej mentioned 一样,您可以使用带有-MemberType 参数的Get-Member cmdlet:

    $documents | Get-Member -MemberType Property | ForEach-Object {
        $_.Name
    }
    

    【讨论】:

    • 如何通过 get-member 获得价值?
    • @Brain2000 你可以从$_.Definition 解析它或者使用$_.Name 来引用你对象的属性:$documents.($_.Name)。但我宁愿选择briantistKolob Canyon的解决方案。
    【解决方案5】:

    我使用以下命令枚举属性并将它们放在一个表中:

    $Object.PSObject.Properties | Format-Table @{ Label = 'Type'; Expression = { "[$($($_.TypeNameOfValue).Split('.')[-1])]" } }, Name, Value -AutoSize -Wrap
    

    我围绕这个编写了以下函数,它也遍历嵌套属性。

    function Enumerate-ObjectProperties {
        param (
            [psobject] $Object,
            [string] $Root
        )
    
        Write-Output $($Object.PSObject.Properties | Format-Table @{ Label = 'Type'; Expression = { "[$($($_.TypeNameOfValue).Split('.')[-1])]" } }, Name, Value -AutoSize -Wrap | Out-String)
    
        foreach ($Property in $Object.PSObject.Properties) {
            # Strings always have a single property "Length". Do not enumerate this.
            if (($Property.TypeNameOfValue -ne 'System.String') -and ($($Object.$($Property.Name).PSObject.Properties))) {
                $NewRoot = $($($Root + '.' + $($Property.Name)).Trim('.'))
                Write-Output "Property: $($NewRoot)"
                Enumerate-ObjectProperties -Object $($Object.$($Property.Name)) -Root $NewRoot
            }
        }
    }
    
    Enumerate-ObjectProperties $YourObject
    

    但是请记住,某些对象的构造有些典型,因为它们的嵌套中有“循环”。引用父对象的子属性。 一个例子是:

    Get-Date 'somebogusdata'
    Enumerate-ObjectProperties $Error[0]
    

    这将创建一个循环。 您当然可以添加一个“深度”参数来添加一个深度计数器,指定您只想进入某个深度。 看起来像这样:

    $script:Level = 1
    function Enumerate-ObjectProperties {
        param (
            [psobject] $Object,
            [int32] $Depth = 1,
            [string] $Root
        )
    
        Write-Output $($Object.PSObject.Properties | Format-Table @{ Label = 'Type'; Expression = { "[$($($_.TypeNameOfValue).Split('.')[-1])]" } }, Name, Value -AutoSize -Wrap | Out-String)
    
        foreach ($Property in $Object.PSObject.Properties) {
            # Strings always have a single property "Length". Do not enumerate this.
            if (($Property.TypeNameOfValue -ne 'System.String') -and ($($Object.$($Property.Name).PSObject.Properties)) -and ($Level -le $Depth)) {
                $NewRoot = $($($Root + '.' + $($Property.Name)).Trim('.'))
                $Level++
                Write-Output "Property: $($NewRoot) (Level: $Level)"
                Enumerate-ObjectProperties -Object $($Object.$($Property.Name)) -Root $NewRoot
                $Level--
            }
        }
    }
    
    Enumerate-ObjectProperties -Object $Error[0] -Depth 2
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-02-12
      • 2020-04-18
      • 2018-03-31
      • 1970-01-01
      • 2016-08-02
      • 1970-01-01
      • 1970-01-01
      • 2016-10-08
      相关资源
      最近更新 更多