gpt4 book ai didi

powershell - 使用变量间接访问 PSObject 属性

转载 作者:行者123 更新时间:2023-12-02 06:13:37 24 4
gpt4 key购买 nike

假设我有这样的 JSON:

  {
"a" : {
"b" : 1,
"c" : 2
}
}
现在 ConvertTo-Json将愉快地创造 PSObjects出于那个。我想访问我可以做的项目 $json.a.b并获得 1 - 很好的嵌套属性。
现在,如果我有字符串 "a.b"问题是如何使用该字符串访问该结构中的相同项目?似乎应该有一些我缺少的特殊语法,例如 &用于动态函数调用,否则您必须使用 Get-Member 自己解释字符串我反复期待。

最佳答案

不,还有没有特殊语法 ,但有一个简单的 解决方法 , 使用 iex , Invoke-Expression 的内置别名[1]小命令:

$propertyPath = 'a.b'

# Note the ` (backtick) before $json, to prevent premature expansion.
iex "`$json.$propertyPath" # Same as: $json.a.b

# You can use the same approach for *setting* a property value:
$newValue = 'foo'
iex "`$json.$propertyPath = `$newValue" # Same as: $json.a.b = $newValue
警告 : 这样做 仅当您完全控制或隐式信任 $propertyPath 的值时 .
仅在极少数情况下是 Invoke-Expression 真正需要,它 should generally be avoided ,因为它可能存在安全风险。
请注意 如果目标属性包含特定集合类型的实例并且您希望按原样保留它(这并不常见) (例如,如果属性值是强类型数组,例如 [int[]] ,或列表类型的实例,例如 [System.Collections.Generic.List`1] ),请使用以下内容:
# "," constructs an aux., transient array that is enumerated by
# Invoke-Expression and therefore returns the original property value as-is.
iex ", `$json.$propertyPath"
没有 , 技术, Invoke-Expression枚举集合值属性的元素,您最终会得到一个常规的 PowerShell 数组,其类型为 [object[]] - 但是,通常情况下,这种区别无关紧要。
注意:如果您要发送 , 的结果技术直接通过管道,集合值的属性值将作为单个对象发送,而不是像往常一样被枚举。 (相比之下,如果您先将结果保存在变量中,然后通过管道将其发送,则会发生通常的枚举)。虽然您可以简单地通过包含 Invoke-Expression 来强制枚举调用 (...) ,没有理由使用 , 考虑到枚举总是会丢失有关正在枚举其元素的集合类型的信息。
继续阅读打包解决方案。

笔记:
  • 以下打包解决方案原用 Invoke-Expression结合清理指定的属性路径,以防止无意/恶意注入(inject)命令。但是,这些解决方案现在使用不同的方法,即将属性路径拆分为单独的属性名称并迭代地深入到对象中,如 Gyula Kokas's helpful answer 所示。 .这不仅不需要 sanitizer ,而且比使用 Invoke-Expression 更快。 (后者仍然值得考虑一次性使用)。
  • 此技术的简洁、仅获取、始终枚举的版本将是以下函数:
    # Sample call: propByPath $json 'a.b'
    function propByPath { param($obj, $propPath) foreach ($prop in $propPath.Split('.')) { $obj = $obj.$prop }; $obj }
  • 下面更详细的解决方案提供了什么:参数验证,还可以通过路径设置属性值,以及 - 在 propByPath 的情况下function - 防止枚举作为集合的属性值的选项(请参阅下一点)。

  • propByPath函数提供了 -NoEnumerate切换到可选地请求保留属性值的特定集合类型。
  • 相比之下,.PropByPath() 中省略了此功能。方法,因为没有语法上方便的方式来请求它(方法只支持位置参数)。一个可能的解决方案是创建第二种方法,比如 .PropByPathNoEnumerate() ,这适用于 , 上面讨论的技术。

  • 辅助函数 propByPath :
    function propByPath {

    param(
    [Parameter(Mandatory)] $Object,
    [Parameter(Mandatory)] [string] $PropertyPath,
    $Value, # optional value to SET
    [switch] $NoEnumerate # only applies to GET
    )

    Set-StrictMode -Version 1

    # Note: Iteratively drilling down into the object turns out to be *faster*
    # than using Invoke-Expression; it also obviates the need to sanitize
    # the property-path string.

    $props = $PropertyPath.Split('.') # Split the path into an array of property names.
    if ($PSBoundParameters.ContainsKey('Value')) { # SET
    $parentObject = $Object
    if ($props.Count -gt 1) {
    foreach ($prop in $props[0..($props.Count-2)]) { $parentObject = $parentObject.$prop }
    }
    $parentObject.($props[-1]) = $Value
    }
    else { # GET
    $value = $Object
    foreach ($prop in $props) { $value = $value.$prop }
    if ($NoEnumerate) {
    , $value
    } else {
    $value
    }
    }

    }
    而不是 Invoke-Expression然后调用您将使用:
    # GET
    propByPath $obj $propertyPath

    # GET, with preservation of the property value's specific collection type.
    propByPath $obj $propertyPath -NoEnumerate


    # SET
    propByPath $obj $propertyPath 'new value'

    你甚至可以 使用 PowerShell 的 ETS (扩展类型系统)到 附上 .PropByPath()所有人的方法[pscustomobject]实例 ( PSv3+ 语法;在 PSv2 中,您必须创建一个 *.types.ps1xml 文件并使用 Update-TypeData -PrependPath 加载它):
    'System.Management.Automation.PSCustomObject',
    'Deserialized.System.Management.Automation.PSCustomObject' |
    Update-TypeData -TypeName { $_ } `
    -MemberType ScriptMethod -MemberName PropByPath -Value { #`

    param(
    [Parameter(Mandatory)] [string] $PropertyPath,
    $Value
    )
    Set-StrictMode -Version 1


    $props = $PropertyPath.Split('.') # Split the path into an array of property names.
    if ($PSBoundParameters.ContainsKey('Value')) { # SET
    $parentObject = $this
    if ($props.Count -gt 1) {
    foreach ($prop in $props[0..($props.Count-2)]) { $parentObject = $parentObject.$prop }
    }
    $parentObject.($props[-1]) = $Value
    }
    else { # GET
    # Note: Iteratively drilling down into the object turns out to be *faster*
    # than using Invoke-Expression; it also obviates the need to sanitize
    # the property-path string.
    $value = $this
    foreach ($prop in $PropertyPath.Split('.')) { $value = $value.$prop }
    $value
    }

    }
    然后您可以拨打 $obj.PropByPath('a.b')$obj.PropByPath('a.b', 'new value') 备注 : 类型 Deserialized.System.Management.Automation.PSCustomObject除了 System.Management.Automation.PSCustomObject为了还涵盖反序列化的自定义对象,这些对象在许多场景中返回,例如使用 Import-CliXml ,从后台作业接收输出,并使用远程处理。 .PropByPath()将在任何 [pscustomobject] 上提供 session 剩余部分的实例(即使是在 Update-TypeData 调用 [2] 之前创建的实例);放置 Update-TypeData 调用您的 $PROFILE (配置文件)使该方法默认可用。

    [1] 注意:虽然通常建议将别名限制为交互式使用并在脚本中使用完整的 cmdlet 名称,但使用 iex对我来说是可以接受的,因为它是一个内置别名并且可以提供简洁的解决方案。
    [2] 验证(全部在一行) $co = New-Object PSCustomObject; Update-TypeData -TypeName System.Management.Automation.PSCustomObject -MemberType ScriptMethod -MemberName GetFoo -Value { 'foo' }; $co.GetFoo() ,输出 foo即使 $co之前创建的 Update-TypeData被称为。

    关于powershell - 使用变量间接访问 PSObject 属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51863251/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com