通过 ValidateScript 属性将默认参数值与验证相结合
Combine a default parameter value with validation via the ValidateScript attribute
我有以下 PowerShell 代码来验证用户输入作为路径,如果用户没有输入任何内容,我会尝试为他们分配一个默认值。然而,当我 运行 这个时, $filePath
变量没有被赋予任何值。
无论如何我可以更改它以在验证进行时为其分配默认值吗?
代码如下:
function validatePath {
Param
(
[ValidateScript({
If ($_ -eq "" -or $_ -eq [String]::Empty) {
$_ = "C:\Install"
$True
}
ElseIf ($_ -match "^([a-z]:\(?:[-\w\.\d])*)") {
$True
}
Else {
Write-Host "Please enter a valid path,$_ is not a valid path."
Write-debug $_.Exception
}
})]
[string]$filePath = "C:\Install"
)
Process
{
Write-Host "The path is "$filePath
}
}
validatePath -filePath $args[0]
我认为您可以删除验证脚本,而是在开始块中执行此操作:
Begin{
If ($filepath -eq "") {
$filepath = "C:\Install"
}
ElseIf ($filepath -notmatch "^([a-z]:\(?:[-\w\.\d])*)") {
Write-Error "Please enter a valid path,$filepath is not a valid path."
}
}
Process{
本回答首先讨论正确使用ValidateScript
属性。
不相关的默认值问题在后面讨论,随后是关于参数展开的可选部分。
Matt 在他对问题的评论中提供了很好的指示:
一个 ValidateScript
脚本块应该只输出一个 布尔值 .
该布尔值告诉 PowerShell 参数值是否被认为有效,它 采取相应的行动。
值得注意的是,脚本块不是意味着:
- 直接赋值给参数变量
- 包含任何其他输出语句,例如
Write-Host
(您不应该使用它来报告 错误 )。
如果脚本块输出(有效)$False
或脚本块抛出异常, PowerShell:
- 中止调用 函数
- 报告 非终止 错误
如果脚本块输出 $False
,您会收到 一条包含脚本的文字内容 的一般错误消息block(不包括封闭的 {
和 }
)- 这可能 对最终用户来说技术性太强 。
PowerShell Core 引入了一个 optional ErrorMessage = "..."
field ValidateScript
和 ValidatePattern
属性;例如,
[ValidateScript({ $_ % 2 -eq 0 }, ErrorMessage = "{0} is not an even number.")]
在Windows Powershell中,建议抛出异常 带有用户友好的错误消息**,在这种情况下,PowerShell 在其错误消息中包含异常文本。
参数的默认值是设计使然未对照验证脚本 - 作为函数创建者,您有责任默认为有效值 - 请参阅 this blog post.
应用于您的示例:
请注意,我使用 '^[a-z]:\[-\w\d\]*$'
作为正则表达式,因为我认为这就是您实际打算使用的内容。
function validatePath {
Param
(
[ValidateScript({
if ($_ -match '^[a-z]:\[-.\w\d\]*$') { return $True }
Throw "'$_' is not a valid local path."
})]
[string] $filePath = "C:\Install"
)
Process
{
"The path is: $filePath"
}
}
现在所有 3 个调用场景都应该按预期工作:
> validatePath # use default value
The path is: C:\Install
> validatePath -filePath C:\MyInstall # valid path
The path is: C:\MyInstall
> validatePath -filePath NotAFullPath # invalid path -> error with custom message
validatePath : Cannot validate argument on parameter 'filePath'.
'NotAFullPath' is not a valid local path.
At line:1 char:24
+ validatePath -filePath NotAFullPath # invalid path
+ ~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [validatePath], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,validatePath
为什么你的默认参数值没有生效:
此问题与验证无关,并且源于您在 validatePath
调用中传递 $args[0]
的事实:
如果脚本本身没有收到参数,$args[0]
是$null
,但它仍然是作为 显式值 传递,因此它通过强制绑定到参数 $filePath
到 空字符串 .
由于传递了显式参数值,因此未使用默认值,并且 $filePath
最终包含空字符串。
由于这是参数绑定在 PowerShell 中的工作方式,我建议不要尝试在您的函数内部解决它,而是改为修复问题调用:
if ([string] $args[0]) { # only true if $args[0] is neither $null nor the empty string
validatePath -filePath $args[0]
} else {
validatePath
}
请注意,通常最好显式声明参数而不是使用 $args
。
可选阅读:使用 splatting 来(select有效地)将参数传递给另一个命令:
作为在上述条件中使用两个单独调用的替代方法,请考虑使用 parameter splatting,它允许您使用单个集合变量传递多个参数,前缀为 @
:
代表多个位置参数的数组。
更常见和更稳健的是,哈希表表示多个命名参数。
这允许您提前动态构建参数集合,并将集合作为一个整体传递给目标命令的单个调用。
在你的情况下,一个快速而肮脏的解决方法是使用 splatting 和 all 参数 ,即传递 $args
通过(注意 @
印记而不是 $
):
validatePath @args
这将简单地传递所有参数,如果有的话,传递给脚本直到 validatePath
,就好像它们是单独指定的一样;如果没有参数传递给脚本,则不会传递任何参数,并且 validatePath
中的 -filePath
默认值 会 生效。
单个参数展开是另一种选择,它是一种将select参数传递给另一个命令的稳健技术 :
# Define a hashtable to hold the parameters, if any, to pass through
# to validatePath() via splatting.
$htPassthruParams = @{}
# If the first script argument is neither $null nor the empty string,
# add a hashtable entry for it that will bind to the -filePath parameter.
if ([string] $args[0]) { $htPassthruParams.Add('filePath', $args[0]) }
# Pass the hashtable with `@`, the splatting operator, to validatePath()
validatePath @htPassthruParams
如果您声明您的脚本也带有显式参数(使用它自己的 param(...)
块),该方法可以通过 使用自动 $PSBoundParameters
字典来确定参数是否被 绑定 ,这避免了检查特定值的需要:
# Define a hashtable to hold the parameters, if any, to pass through
# to validatePath() via splatting.
$htPassthruParams = @{}
# Using a list of parameters, pass their values through only if they are
# *bound*, i.e., only if they received values when the enclosing script/function
# itself was called.
# Assume that the enclosing script declared a -filePath parameter too.
foreach($paramName in , 'filePath') {
if ($PSBoundParameters.ContainsKey($paramName)) {
$htPassthruParams.Add($paramName, $PSBoundParameters[$paramName])
}
}
# Pass the hashtable with `@`, the splatting operator, to validatePath()
validatePath @htPassthruParams
我有以下 PowerShell 代码来验证用户输入作为路径,如果用户没有输入任何内容,我会尝试为他们分配一个默认值。然而,当我 运行 这个时, $filePath
变量没有被赋予任何值。
无论如何我可以更改它以在验证进行时为其分配默认值吗?
代码如下:
function validatePath {
Param
(
[ValidateScript({
If ($_ -eq "" -or $_ -eq [String]::Empty) {
$_ = "C:\Install"
$True
}
ElseIf ($_ -match "^([a-z]:\(?:[-\w\.\d])*)") {
$True
}
Else {
Write-Host "Please enter a valid path,$_ is not a valid path."
Write-debug $_.Exception
}
})]
[string]$filePath = "C:\Install"
)
Process
{
Write-Host "The path is "$filePath
}
}
validatePath -filePath $args[0]
我认为您可以删除验证脚本,而是在开始块中执行此操作:
Begin{
If ($filepath -eq "") {
$filepath = "C:\Install"
}
ElseIf ($filepath -notmatch "^([a-z]:\(?:[-\w\.\d])*)") {
Write-Error "Please enter a valid path,$filepath is not a valid path."
}
}
Process{
本回答首先讨论正确使用ValidateScript
属性。
不相关的默认值问题在后面讨论,随后是关于参数展开的可选部分。
Matt 在他对问题的评论中提供了很好的指示:
一个
ValidateScript
脚本块应该只输出一个 布尔值 .
该布尔值告诉 PowerShell 参数值是否被认为有效,它 采取相应的行动。值得注意的是,脚本块不是意味着:
- 直接赋值给参数变量
- 包含任何其他输出语句,例如
Write-Host
(您不应该使用它来报告 错误 )。
如果脚本块输出(有效)
$False
或脚本块抛出异常, PowerShell:- 中止调用 函数
- 报告 非终止 错误
如果脚本块输出
$False
,您会收到 一条包含脚本的文字内容 的一般错误消息block(不包括封闭的{
和}
)- 这可能 对最终用户来说技术性太强 。PowerShell Core 引入了一个 optional
ErrorMessage = "..."
fieldValidateScript
和ValidatePattern
属性;例如,
[ValidateScript({ $_ % 2 -eq 0 }, ErrorMessage = "{0} is not an even number.")]
在Windows Powershell中,建议抛出异常 带有用户友好的错误消息**,在这种情况下,PowerShell 在其错误消息中包含异常文本。
参数的默认值是设计使然未对照验证脚本 - 作为函数创建者,您有责任默认为有效值 - 请参阅 this blog post.
应用于您的示例:
请注意,我使用 '^[a-z]:\[-\w\d\]*$'
作为正则表达式,因为我认为这就是您实际打算使用的内容。
function validatePath {
Param
(
[ValidateScript({
if ($_ -match '^[a-z]:\[-.\w\d\]*$') { return $True }
Throw "'$_' is not a valid local path."
})]
[string] $filePath = "C:\Install"
)
Process
{
"The path is: $filePath"
}
}
现在所有 3 个调用场景都应该按预期工作:
> validatePath # use default value
The path is: C:\Install
> validatePath -filePath C:\MyInstall # valid path
The path is: C:\MyInstall
> validatePath -filePath NotAFullPath # invalid path -> error with custom message
validatePath : Cannot validate argument on parameter 'filePath'.
'NotAFullPath' is not a valid local path.
At line:1 char:24
+ validatePath -filePath NotAFullPath # invalid path
+ ~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [validatePath], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,validatePath
为什么你的默认参数值没有生效:
此问题与验证无关,并且源于您在 validatePath
调用中传递 $args[0]
的事实:
如果脚本本身没有收到参数,
$args[0]
是$null
,但它仍然是作为 显式值 传递,因此它通过强制绑定到参数$filePath
到 空字符串 .由于传递了显式参数值,因此未使用默认值,并且
$filePath
最终包含空字符串。
由于这是参数绑定在 PowerShell 中的工作方式,我建议不要尝试在您的函数内部解决它,而是改为修复问题调用:
if ([string] $args[0]) { # only true if $args[0] is neither $null nor the empty string
validatePath -filePath $args[0]
} else {
validatePath
}
请注意,通常最好显式声明参数而不是使用 $args
。
可选阅读:使用 splatting 来(select有效地)将参数传递给另一个命令:
作为在上述条件中使用两个单独调用的替代方法,请考虑使用 parameter splatting,它允许您使用单个集合变量传递多个参数,前缀为 @
:
代表多个位置参数的数组。
更常见和更稳健的是,哈希表表示多个命名参数。
这允许您提前动态构建参数集合,并将集合作为一个整体传递给目标命令的单个调用。
在你的情况下,一个快速而肮脏的解决方法是使用 splatting 和 all 参数 ,即传递 $args
通过(注意 @
印记而不是 $
):
validatePath @args
这将简单地传递所有参数,如果有的话,传递给脚本直到 validatePath
,就好像它们是单独指定的一样;如果没有参数传递给脚本,则不会传递任何参数,并且 validatePath
中的 -filePath
默认值 会 生效。
单个参数展开是另一种选择,它是一种将select参数传递给另一个命令的稳健技术 :
# Define a hashtable to hold the parameters, if any, to pass through
# to validatePath() via splatting.
$htPassthruParams = @{}
# If the first script argument is neither $null nor the empty string,
# add a hashtable entry for it that will bind to the -filePath parameter.
if ([string] $args[0]) { $htPassthruParams.Add('filePath', $args[0]) }
# Pass the hashtable with `@`, the splatting operator, to validatePath()
validatePath @htPassthruParams
如果您声明您的脚本也带有显式参数(使用它自己的 param(...)
块),该方法可以通过 使用自动 $PSBoundParameters
字典来确定参数是否被 绑定 ,这避免了检查特定值的需要:
# Define a hashtable to hold the parameters, if any, to pass through
# to validatePath() via splatting.
$htPassthruParams = @{}
# Using a list of parameters, pass their values through only if they are
# *bound*, i.e., only if they received values when the enclosing script/function
# itself was called.
# Assume that the enclosing script declared a -filePath parameter too.
foreach($paramName in , 'filePath') {
if ($PSBoundParameters.ContainsKey($paramName)) {
$htPassthruParams.Add($paramName, $PSBoundParameters[$paramName])
}
}
# Pass the hashtable with `@`, the splatting operator, to validatePath()
validatePath @htPassthruParams