gpt4 book ai didi

powershell - Powershell:使用变量在脚本 block 中引用$ _的属性

转载 作者:行者123 更新时间:2023-12-02 23:03:28 27 4
gpt4 key购买 nike

$var =@(  @{id="1"; name="abc"; age="1"; },
@{id="2"; name="def"; age="2"; } );
$properties = @("ID","Name","Age") ;
$format = @();
foreach ($p in $properties)
{
$format += @{label=$p ; Expression = {$_.$p}} #$_.$p is not working!
}
$var |% { [PSCustomObject]$_ } | ft $format

在上面的示例中,我想通过变量名称访问每个对象的属性。但是它不能按预期工作。因此,就我而言,如何制作
Expression = {$_.$p}

加工?

最佳答案

OP的代码和此答案使用 PSv3 + 语法。 PSv2不支持将哈希表转换为[pscustomobject],但是您可以将[pscustomobject] $_替换为New-Object PSCustomObject -Property $_

与过去的许多情况一样,PetSerAl用简洁(但非常有帮助)的注释提供了答案。让我详细说明:

您的问题不是您使用变量($p)本身访问属性,而该属性确实起作用(例如$p = 'Year'; Get-Date | % { $_.$p })。

相反,问题在于在$p调用的上下文中,直到稍后的才对脚本块{ $_.$p }中的 Format-Table求值,这意味着对于所有输入对象使用相同的固定值-即$p的值在这一点上(恰好是$p循环中分配给foreach的最后一个值)。

最干净,最通用的解决方案是在脚本块上调用 .GetNewClosure() ,以将脚本块中的$p绑定(bind)到当时最新的循环迭代特定值

$format += @{ Label = $p; Expression = { $_.$p }.GetNewClosure() }

docs(添加了重点):

In this case, the new script block is closed over the local variables in the scope that the closure is defined in. In other words, the current values of the local variables are captured and enclosed inside the script block that is bound to the module.



请注意,自动变量$_foreach循环内未定义(PowerShell仅在某些上下文中将其定义为手边的输入对象,例如在传递给管道中cmdlet的脚本块中),因此根据需要,它保持未绑定(bind)状态。

警告:
  • 尽管上面使用的.GetNewClosure()很方便,但它具有效率低下的缺点,就是始终捕获所有局部变量,而不仅仅是捕获所需的局部变量。
  • 一种效率更高的替代,可以避免此问题-尤其是可以避免(从Windows PowerShell v5.1.14393.693和PowerShell Core v6.0.0-alpha.15开始)的错误,在该错误中可以关闭局部变量中断,即当封闭的脚本/函数具有带有验证属性(例如[ValidateNotNull()])的参数且该参数未绑定(bind)(未传递任何值)时 [1]-以下内容,更复杂的表示法PetSerAl和Burt_Harris的答案here
    :
    $format += @{ Label = $p; Expression = & { $p = $p; { $_.$p }.GetNewClosure() } }
  • & { ... }用自己的局部变量创建一个子作用域。
  • 然后,
  • $p = $p从其继承的值创建一个本地$p变量。
    要推广这种方法,必须为脚本块中引用的每个变量包含这样的语句。
  • 然后
  • { $_.$p }.GetNewClosure()输出一个脚本块,该脚本块关闭子作用域的局部变量(在这种情况下仅为$p)。
  • 该错误已报告为an issue in the PowerShell Core GitHub repository,并且自been fixed起已经存在-我尚不清楚该修补程序将发布哪个版本。
  • 在简单的情况下,mjolinor's answer可以这样做:它通过扩展字符串间接创建脚本块,该字符串原本包含当时的$p值,但是请注意,这种方法难以一概而论,因为仅对变量值进行字符串化通常不能保证它可以作为PowerShell源代码的一部分(扩展字符串必须求值才能转换为脚本块)。

  • 放在一起:
    # Sample array of hashtables.
    # Each hashtable will be converted to a custom object so that it can
    # be used with Format-Table.
    $var = @(
    @{id="1"; name="abc"; age="3" }
    @{id="2"; name="def"; age="4" }
    )

    # The array of properties to output, which also serve as
    # the case-exact column headers.
    $properties = @("ID", "Name", "Age")

    # Construct the array of calculated properties to use with Format-Table:
    # an array of output-column-defining hashtables.
    $format = @()
    foreach ($p in $properties)
    {
    # IMPORTANT: Call .GetNewClosure() on the script block
    # to capture the current value of $p.
    $format += @{ Label = $p; Expression = { $_.$p }.GetNewClosure() }
    # OR: For efficiency and full robustness (see above):
    # $format += @{ Label = $p; Expression = & { $p = $p; { $_.$p }.GetNewClosure() } }
    }

    $var | ForEach-Object { [pscustomobject] $_ } | Format-Table $format

    这样产生:

    ID Name Age
    -- ---- ---
    1 abc 3
    2 def 4

    根据需要:输出列使用$properties中指定的列标签,同时包含正确的值。

    请注意,为了清楚起见,我如何删除了不必要的;实例,并用基础cmdlet名称替换了内置别名%ft。我还分配了不同的age值,以更好地证明输出正确。

    在此特定情况下,较简单的解决方案:

    要按原样引用属性值而不进行转换,只需将属性的名称用作计算出的属性(列格式哈希表)中的Expression条目即可。换句话说:在这种情况下,您不需要包含表达式([scriptblock])的{ ... }实例,只需要包含属性名称的[string]值。

    因此,以下内容也可以工作:
    # Use the property *name* as the 'Expression' entry's value.
    $format += @{ Label = $p; Expression = $p }

    请注意,这种方法是为了避免原始问题,因为$p是在分配时进行评估的,因此将捕获特定于循环迭代的值。

    [1]重现:调用function foo { param([ValidateNotNull()] $bar) {}.GetNewClosure() }; foo.GetNewClosure()失败,错误Exception calling "GetNewClosure" with "0" argument(s): "The attribute cannot be added because variable bar with value would no longer be valid."也就是说,尝试在闭包中包含未绑定(bind)的-bar参数值-$bar变量,该值显然然后默认为$null,这违反了其验证属性。
    传递有效的-bar值可以解决问题;例如foo -bar ''
    考虑此错误的基本原理:如果函数本身在不存在$bar参数值的情况下将-bar视为不存在,则.GetNewClosure()也应如此。

    关于powershell - Powershell:使用变量在脚本 block 中引用$ _的属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42183916/

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