$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}
型
工作吗
3条答案
按热度按时间4ioopgfo1#
OP的代码和这个答案使用PSv 3 +语法。PSv 2不支持将哈希表转换为
[pscustomobject]
,但您可以将[pscustomobject] $_
替换为New-Object PSCustomObject -Property $_
。与过去的许多情况一样,PetSerAl以简洁(但非常有用)的评论方式提供了答案;让我详细说明:
你的问题 * 不是 * 你正在使用一个变量(
$p
)来访问一个属性 * 本身 ,它 * 确实 * 工作(例如,$p = 'Year'; Get-Date | % { $_.$p }
)。相反,问题在于脚本块
{ $_.$p }
中的*$p
直到 * 稍后 * 才在Format-Table
调用的上下文中进行计算,这意味着相同,固定值用于所有输入对象**-即$p
* 在该点的值 (这恰好是在foreach
循环中分配给$p
的最后一个值)。最干净和最通用的解决方案是在脚本块上调用*
.GetNewClosure()
**,将脚本块中的$p
绑定到 then-current,循环迭代特定的值。字符串
从docs(强调添加; * 更新 :引用的段落已被删除,但仍然适用):
在这种情况下,新的脚本块在定义闭包的范围内对 * local variables进行闭包。换句话说,*local 变量的当前值被捕获并封装在绑定到模块的脚本块内。
请注意,automatic 变量
$_
在foreach
循环中是未定义的(PowerShell仅在某些上下文中将其定义为手头的输入对象,例如在管道中传递给小程序的脚本块中),因此它仍然保持 * 未绑定 *。注意事项:
.GetNewClosure()
很方便,但它有效率低下的缺点,总是捕获 * 所有 * 局部变量,而不仅仅是所需的变量;此外,返回的脚本块在为这种情况创建的动态(内存中)模块中运行。[ValidateNotNull()]
*,并且 * 该参数 * 未绑定 * 时,(没有值被传递)[1] -是下面的,明显更复杂的表达式再次向PetSerAl提示帽子,Burt_Harris的答案here:型
& { ... }
创建一个带有自己的局部变量的 * 子作用域 *。$p = $p
从它的 inherited 值创建一个 local$p
变量。为了推广这种方法,* 您必须为脚本块中引用的每个变量都包含这样的语句 *。
{ $_.$p }.GetNewClosure()
输出一个脚本块,它覆盖子作用域的局部变量(在本例中仅为$p
)。$p
值 *,但请注意,该方法 * 难以推广 *,因为仅仅字符串化变量值通常不能保证它作为PowerShell * 源代码 * 的一部分工作(展开的字符串必须计算为该值,以便转换为脚本块)。把它们放在一起:
型
这产生:
型
如所需:输出列使用
$properties
中指定的列标签,同时包含正确的值。请注意,为了清楚起见,我删除了不必要的
;
示例,并将内置别名%
和ft
替换为底层的小程序名称。我还分配了不同的age
值,以更好地证明输出是正确的。更简单的解决方案,在此 * 具体 * 情况下:
若要引用属性值 * 按原样 , 不进行转换 *,则在计算属性中使用属性的 * 名称 * 作为
Expression
条目即可(列格式化哈希表)。换句话说:你不需要一个包含 expression 的[scriptblock]
示例,在这种情况下({ ... }
),只需要一个包含属性 name 的[string]
值。因此,以下做法也会奏效:
型
请注意,这种方法恰好 * 避免 * 了原来的问题,因为
$p
是 * 在赋值时 * 计算的,因此捕获了循环迭代特定的值。[1]重现:当调用
.GetNewClosure()
时,function foo { param([ValidateNotNull()] $bar) {}.GetNewClosure() }; foo
失败,错误为Exception calling "GetNewClosure" with "0" argument(s): "The attribute cannot be added because variable bar with value would no longer be valid."
也就是说,试图在闭包中包含 unbound
-bar
参数值($bar
变量),这显然会默认为$null
,这违反了它的验证属性。传递一个有效的
-bar
值可以解决问题;例如foo -bar ''
。认为这是一个 bug 的理由是:如果 * 函数本身 * 在没有
-bar
参数值的情况下将$bar
视为不存在,那么.GetNewClosure()
也应该如此。dauxcl2d2#
虽然整个方法对于给定的例子似乎是错误的,但正如使其工作的练习一样,关键是在正确的时间控制变量扩展。在
foreach
循环中,$_
为null($_
仅在管道中有效)。您需要等待,直到它到达Foreach-Object
循环才尝试评估它。这似乎可以用最小量的重构来工作:
字符串
从可扩展字符串创建脚本块将允许
$p
为每个属性名扩展。转义$_
将使其在字符串中保持为文字,直到它被呈现为脚本块,然后在ForEach-Object
循环中进行计算。j8yoct9x3#
在一个Hash表数组中添加任何内容都会有点挑剔,但是你的变量扩展是这样纠正的:
字符串
你需要另一个循环才能将它绑定到数组中的特定项。也就是说,我认为使用Object数组是一种更简洁的方法-但我不知道你在处理什么,确切地说。