PowerShell 管道参数绑定顺序
PowerShell pipeline parameter binding order
我有一个可以接受两种管道数据的高级功能:
- PSTypeName 为 "MyType"
的自定义对象
- ID 为 属性
的任何对象
这是我的函数:
function Test-PowerShell {
[CmdletBinding(DefaultParameterSetName = "ID")]
param (
[Parameter(
Mandatory = $true,
ParameterSetName = "InputObject",
ValueFromPipeline = $true
)]
[PSTypeName('MyType')]
$InputObject,
[Parameter(
Mandatory = $true,
ParameterSetName = 'ID',
ValueFromPipelineByPropertyName = $true
)]
[int]
$ID
)
process {
if ($InputObject) {
$objects = $InputObject
Write-Verbose 'InputObject binding'
}
else {
$objects = Get-MyType -ID $ID
Write-Verbose 'ID binding'
}
# Do something with $objects
}
}
我可以这样使用这个函数:
$obj = [PSCustomObject]@{
PSTypeName = 'MyType'
ID = 5
Name = 'Bob'
}
$obj | Test-PowerShell -Verbose
请注意,此对象满足以上两个条件:它是一个 MyType,并且具有 ID 属性。在这种情况下,PowerShell 始终绑定到 ID 属性。这在性能方面并不理想,因为管道对象被丢弃,我必须使用 ID 重新查询它。我的问题是:
如果可能,如何强制 PowerShell 将管道绑定到 $InputObject?
如果我将默认参数集更改为 InputObject,PowerShell 将绑定到 $InputObject。但是,我不希望这样,因为当 运行 没有参数时,我希望 PowerShell 提示输入 ID,而不是 InputObject。
简单的答案:删除 $InputObject
上 Parameter
属性的 Mandatory
参数以获得您想要的功能。我没有足够的知识来解释参数绑定的工作原理为什么这有效。
function Test-PowerShell {
[CmdletBinding(DefaultParameterSetName = 'ID')]
param(
[Parameter(ParameterSetName = 'InputObject', ValueFromPipeline)]
[PSTypeName('MyType')]
$InputObject,
[Parameter(ParameterSetName = 'ID', Mandatory, ValueFromPipelineByPropertyName)]
[int]
$ID
)
process {
$PSBoundParameters
}
}
$o = [pscustomobject]@{
PSTypeName = 'MyType'
ID = 6
Name = 'Bob'
}
PS> $o | Test-PowerShell
Key Value
--- -----
InputObject MyType
PS> [pscustomobject]@{ID = 6} | Test-PowerShell
Key Value
--- -----
ID 6
下面的想法和实验。
这里有 a 解决您的问题(定义您自己的类型):
Add-Type -TypeDefinition @'
public class MyType
{
public int ID { get; set; }
public string Name { get; set; }
}
'@
然后您将参数标记为 [MyType]
,像从 [pscustomobject]
:
创建对象一样
[MyType]@{ ID = 6; Name = 'Bob' }
事后看来,这个方法行不通。你 运行 喜欢的是 DefaultParameterSet
的行为。我建议更改您作为管道输入的内容。是否有将 ID 作为管道输入与用户仅使用 Test-PowerShell -ID 5
或 Test-PowerShell
并提示输入 ID 的用例?
根据我的测试,这里有一个建议可能会如您所愿:
function Test-PowerShell {
[CmdletBinding(DefaultParameterSetName = 'ID')]
param(
[Parameter(ParameterSetName = 'InputObject', Mandatory = $true, ValueFromPipeline = $true)]
[PSTypeName('MyType')]
$InputObject,
[Parameter(ParameterSetName = 'ID', Mandatory = $true, ValueFromPipeline = $true)]
[int]
$ID
)
process {
$PSBoundParameters
}
}
以现有的内置 cmdlet 为例,它们不会在一个对象上为多个参数使用相同的名称或属性。在 Get-ChildItem
中,LiteralPath
和 Path
都接受管道输入,但 LiteralPath
仅通过 PropertyName LiteralPath
或 PSPath
(别名)接受它。 Path
是 ByValue 和 PropertyName,但仅作为 Path
.
我有一个可以接受两种管道数据的高级功能:
- PSTypeName 为 "MyType" 的自定义对象
- ID 为 属性 的任何对象
这是我的函数:
function Test-PowerShell {
[CmdletBinding(DefaultParameterSetName = "ID")]
param (
[Parameter(
Mandatory = $true,
ParameterSetName = "InputObject",
ValueFromPipeline = $true
)]
[PSTypeName('MyType')]
$InputObject,
[Parameter(
Mandatory = $true,
ParameterSetName = 'ID',
ValueFromPipelineByPropertyName = $true
)]
[int]
$ID
)
process {
if ($InputObject) {
$objects = $InputObject
Write-Verbose 'InputObject binding'
}
else {
$objects = Get-MyType -ID $ID
Write-Verbose 'ID binding'
}
# Do something with $objects
}
}
我可以这样使用这个函数:
$obj = [PSCustomObject]@{
PSTypeName = 'MyType'
ID = 5
Name = 'Bob'
}
$obj | Test-PowerShell -Verbose
请注意,此对象满足以上两个条件:它是一个 MyType,并且具有 ID 属性。在这种情况下,PowerShell 始终绑定到 ID 属性。这在性能方面并不理想,因为管道对象被丢弃,我必须使用 ID 重新查询它。我的问题是:
如果可能,如何强制 PowerShell 将管道绑定到 $InputObject?
如果我将默认参数集更改为 InputObject,PowerShell 将绑定到 $InputObject。但是,我不希望这样,因为当 运行 没有参数时,我希望 PowerShell 提示输入 ID,而不是 InputObject。
简单的答案:删除 $InputObject
上 Parameter
属性的 Mandatory
参数以获得您想要的功能。我没有足够的知识来解释参数绑定的工作原理为什么这有效。
function Test-PowerShell {
[CmdletBinding(DefaultParameterSetName = 'ID')]
param(
[Parameter(ParameterSetName = 'InputObject', ValueFromPipeline)]
[PSTypeName('MyType')]
$InputObject,
[Parameter(ParameterSetName = 'ID', Mandatory, ValueFromPipelineByPropertyName)]
[int]
$ID
)
process {
$PSBoundParameters
}
}
$o = [pscustomobject]@{
PSTypeName = 'MyType'
ID = 6
Name = 'Bob'
}
PS> $o | Test-PowerShell
Key Value
--- -----
InputObject MyType
PS> [pscustomobject]@{ID = 6} | Test-PowerShell
Key Value
--- -----
ID 6
下面的想法和实验。
这里有 a 解决您的问题(定义您自己的类型):
Add-Type -TypeDefinition @'
public class MyType
{
public int ID { get; set; }
public string Name { get; set; }
}
'@
然后您将参数标记为 [MyType]
,像从 [pscustomobject]
:
[MyType]@{ ID = 6; Name = 'Bob' }
事后看来,这个方法行不通。你 运行 喜欢的是 DefaultParameterSet
的行为。我建议更改您作为管道输入的内容。是否有将 ID 作为管道输入与用户仅使用 Test-PowerShell -ID 5
或 Test-PowerShell
并提示输入 ID 的用例?
根据我的测试,这里有一个建议可能会如您所愿:
function Test-PowerShell {
[CmdletBinding(DefaultParameterSetName = 'ID')]
param(
[Parameter(ParameterSetName = 'InputObject', Mandatory = $true, ValueFromPipeline = $true)]
[PSTypeName('MyType')]
$InputObject,
[Parameter(ParameterSetName = 'ID', Mandatory = $true, ValueFromPipeline = $true)]
[int]
$ID
)
process {
$PSBoundParameters
}
}
以现有的内置 cmdlet 为例,它们不会在一个对象上为多个参数使用相同的名称或属性。在 Get-ChildItem
中,LiteralPath
和 Path
都接受管道输入,但 LiteralPath
仅通过 PropertyName LiteralPath
或 PSPath
(别名)接受它。 Path
是 ByValue 和 PropertyName,但仅作为 Path
.