提问人:Lieven Keersmaekers 提问时间:4/15/2023 最后编辑:Lieven Keersmaekers 更新时间:4/20/2023 访问量:104
Powershell 枚举参数不会阻止传递多个参数
Powershell enum parameter does not prevent multiple parameters being passed
问:
我假设枚举参数默认情况下只接受一个参数,并且在尝试传递两个或多个参数时会抛出异常。
enum Test {one; two; three}
function Test-Enum {[CmdletBinding()] param (
[Test]$number
) $number}
这按预期工作
Test-Enum -number one
Test-Enum -number two
Test-Enum -number three
我本来以为会有一个异常,试图传递两个参数
Test-Enum -number one, two # outputs two
Test-Enum -number two, one # outputs two
Test-Enum -number one, three # outputs three
test-Enum -number three, one # outputs three
这些确实会触发异常
Test-Enum -number two, three # Cannot convert value error
Test-Enum -number three, two # Cannot convert value error
我在这里遗漏了一些明显的东西,但我无法弄清楚。如何防止使用多个参数调用 Test-Enum?
PS F:\> $PSVersionTable
Name Value
---- -----
PSVersion 5.1.19041.2673
PSEdition Desktop
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
BuildVersion 10.0.19041.2673
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
答:
TL的;博士
至少在 PowerShell 7.3.4 (撰写本文时是最新的) 中,PowerShell 的参数绑定器将不具有 [Flags] 属性的 [
枚举
] 派生类型(这使它们成为可以组合其值的位字段)视为具有[Flags]
属性的类型。这使得它总是接受多个值,这不适用于非枚举类型。
[Flags]
- 有关此问题行为的讨论,请参阅 GitHub 问题 #19513。
遗憾的是,PowerShell 参数绑定器允许将多个值传递给类型化参数,即使特定类型不是 [Flags]
修饰的类型(在这种情况下,接受多个值很有帮助且合适)。[enum]
如果给定多个值,则这些值是位或值,如果生成的数字与定义的值不对应,则会发生错误。
您可以从字符串强制转换以获得等效的行为,以了解参数绑定器的作用(枚举值的数值隐式为 、 for 和 for):0
one
1
two
2
three
# -> Same as: [Test] (0 -bor 1) -> two
[Test] 'one, two'
# -> Same as [Test] (1 -bor 2), which FAILS, because 1 -bor 2 is 3,
# and 3 isn't a defined value.
[Test] 'two, three'
虽然此行为适用于基于枚举的类型,但不适用于那些不适用的枚举类型。[Flags]
Windows PowerShell 的解决方法:
解决方法很麻烦:
将参数键入为 ,这将自动阻止传递多个值。
[string]
使用属性验证传递的字符串是否是枚举中值的名称。
[ValidateScript({ … })]
[Test]
为了也支持 Tab 自动补全,请添加一个属性:
[ArgumentCompleter()]
enum Test { one; two; three }
function Test-Enum {
[CmdletBinding()] param (
[ValidateScript({
if ($_ -as [Test] -ne $null) { return $true }
throw "$_ is not a valid value. Try one of: $([enum]::GetNames([Test]))"
})]
[ArgumentCompleter({
param($unused1, $unused2, $wordToComplete)
[enum]::GetNames([Test]) -like "$wordToComplete*"
})]
[string] $number
)
# Now it is safe to convert the [string] value to [Test]
$enumVal = [Test] $number
$enumVal
}
备选方案:
- 如果非必须使用 -派生类型,则可以使用
[ValidateSet()]
属性并将可接受的值枚举为其中的字符串。
但是,在以后使用参数值时,这并不能为您提供类型安全性。[enum]
PowerShell (Core) 7+ 的解决方法:
使用实现 System.Management.Automation.IValidateSetValuesGenerator
接口接口的辅助类,该接口也可以与该接口一起使用,并且可以在其中用于生成有效名称:[ValidateSet()]
[enum]::GetNames([Test])
# PS v7+ only
enum Test {one; two; three}
# Helper class that implements IValidateSetValuesGenerator
# and returns the valid enumeration values, to be used with [ValidateSet] below.
class ValidTestEnumNames : System.Management.Automation.IValidateSetValuesGenerator {
[string[]] GetValidValues() {
return [enum]::GetNames([Test])
}
}
function Test-Enum {
[CmdletBinding()]
param (
[ValidateSet([ValidTestEnumNames])] # Use the class defined above.
[string] $number
)
$enumVal = [Test] $number
$enumVal
}
评论