我创建了一个高级功能,以从在VMware ESXi上运行的VM获取MAC地址。
function Get-MacFromVm {
[CmdletBinding(SupportsShouldProcess=$true)]
Param(
# The name of the VM of which we want to obtain the mac address.
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[string[]]
$Name
)
Begin {}
Process {
foreach ($item in $Name) {
if ($PSCmdlet.ShouldProcess($item, "Getting the mac address")) {
Get-VM $item -PipelineVariable vm |
Get-NetworkAdapter |
Select-Object @{n="Name"; e={$vm.Name}},
@{n="ClientId"; e={$_.MacAddress -replace ":","-"}}
}
}
}
End {}
}
到目前为止,一切正常。
我可以通过以下任何一种方式使用它并获得结果。
它通过命名参数或作为管道输入接受单个字符串或字符串数组。
Get-MacFromVm -Name "playground"
Get-MacFromVm -Name "playground", "DC01"
"playground", "DC01" | Get-MacFromVm
输出是带有2个属性(名称和ClientId)的
[PSCustomObject]
。现在,当我想通过使用
-PipelineVariable
参数将结果链接到多个其他cmdlet时,问题就开始了。通常我应该可以这样使用它:
Get-MacFromVm -Name "playground" -PipelineVariable pv | % {$pv}
但这并没有显示任何结果。如果我将
$pv
替换为$_
,它的确显示了正确的结果,但我不能在管道链中更远的地方使用该自动变量2或3 cmdlet。尽管我可以通过使用
-OutVariable
和/或将其拆分为多行来解决此问题。我想知道为什么这行不通,我想知道我在这里缺少什么。
最佳答案
我没有-PipelineVariable
参数的经验。因此,我以此为契机学习了-PipelineVariable
参数:
由于我也无法轻松访问VM,因此我对该功能进行了如下模拟:
Function Get-MacFromVm {
[CmdletBinding(SupportsShouldProcess=$true)]
Param(
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[string[]]
$Name
)
Function MacAddress {(1..4 | ForEach {'{0:x2}' -f (Get-Random -Min 0 -Max 255)}) -Join ":"}
Function Get-VM {[cmdletbinding()] Param ($Name) [pscustomobject]@{Name = $Name; MacAddress = (MacAddress)}}
ForEach ($Item in $Name) {
If ($PSCmdlet.ShouldProcess($Item, "Getting the mac address")) {
Get-VM $item -PipelineVariable vm |
Select-Object @{n="Name"; e={$vm.Name}}, @{n="ClientId"; e={$_.MacAddress -replace ":","-"}}
}
}
}
但我无法重现该问题:
PS C:\> Get-MacFromVm -Name "playground", "DC01" -PipelineVariable pv | % {$pv}
Name ClientId
---- --------
playground 3c-23-55-c4
DC01 4f-38-42-a7
因此,我想知道此模拟功能是否也适用于您。
如果是的话,也许您可以找到一个真正的VM对象的差异。
了解PowerShell,我会质疑是否没有输出或没有显示任何内容。那么,如果仅输出单个属性,将会发生什么:
Get-MacFromVm -Name "playground" -PipelineVariable pv | % {$pv.Name}
要么:
Get-MacFromVm -Name "playground" -PipelineVariable pv | % {$pv.GetType()}
这会返回“
You cannot call a method on a null-valued expression.
”错误吗?更新2019年10月30日
根据mklement0的注释:
您没有看到the bug的原因是由于未使用
begin
/ process
/ end
块,您的函数隐式地在end
块中运行,并且所有这些脚本块的第一个调用都在管道变量中捕获,但是而不是后续脚本块调用的输出,这会在具有process
块的函数中发生。意味着正确的模拟应该是:
Function Get-MacFromVm {
[CmdletBinding(SupportsShouldProcess=$true)]
Param(
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[string[]]
$Name
)
Begin {
Function MacAddress {(1..4 | ForEach {'{0:x2}' -f (Get-Random -Min 0 -Max 255)}) -Join ":"}
Function Get-VM {[cmdletbinding()] Param ($Name) [pscustomobject]@{Name = $Name; MacAddress = (MacAddress)}}
}
Process {
ForEach ($Item in $Name) {
If ($PSCmdlet.ShouldProcess($Item, "Getting the mac address")) {
Get-VM $item -PipelineVariable vm |
Select-Object @{n="Name"; e={$vm.Name}}, @{n="ClientId"; e={$_.MacAddress -replace ":","-"}}
}
}
}
}
的确对于
PipelineVariable
没有任何结果:PS C:\> Get-MacFromVm -Name "playground", "DC01" -PipelineVariable pv | % {$pv}
PS C:\>
当前项目(
$_
)可以:PS C:\> Get-MacFromVm -Name "playground", "DC01" -PipelineVariable pv | % {$_}
Name ClientId
---- --------
playground e7-b5-a0-31
DC01 0f-ed-94-cc