如何解决如何正确使用PowerShell $args[]
有时使用包含空格并用双引号括起来的参数调用 PowerShell 脚本, 以确保字符串被解释为一个单一的参数。当参数为空并且传递的双引号不包含任何内容时,就会出现问题。 PS 将命令行视为非空,并尝试从中形成参数值。除了双引号被去掉,留下一个空字符串作为参数值。当然 PS 不能实际使用空值,所以我们要验证命令行参数以禁止空字符串。
查看调用 Cmd 脚本和 PS 源代码,两者都进行了注释。测试 4 和 5 显示了扫描 $args[] 数组并返回第一个有效/非空参数的函数的好处。
问题:为什么不能只用一个 ForEach 循环对函数进行编码?
Args.CMD:
@Echo Off
CD /D "%~dp0" & Rem Change to the folder where this script lives
CLS
Echo.
Rem PS script name is the same as this Cmd script
Set Script=%~n0.PS1
Rem Parameters of each call here are TestNumber,ExpectedExitCode,Argument1,Argument2
Rem Test 0: Try with zero arguments; expected exit code is 1
Call :TryMe 0 1
Rem Test 1: Try with non-useful arguments; expected exit code is 2
Call :TryMe 1 2 "" ""
Rem Test 2: Try with valid arguments,single words; expected exit code is 0
Call :TryMe 2 0 ArgOne ArgTwo
Rem Test 3: Try with valid arguments,multiple words; expected exit code is 0
Call :TryMe 3 0 "Arg One" "Arg Two"
Rem Test 4: Try with mixed arguments,single words; expected exit code is 0
Call :TryMe 4 0 "" ArgTwo
Rem Test 5: Try with mixed arguments,multiple words; expected exit code is 0
Call :TryMe 5 0 "" "Arg Two"
Echo All Done.
Pause
GoTo :EOF
:TryMe
Set TstNbr=%1
Set Expcod=%2
Set Args12=%3 %4
Echo Test %TstNbr% of 5: PowerShell.EXE -F %script% %Args12%
PowerShell.EXE -F %script% %Args12%
Echo ErrorLevel is [%ErrorLevel%],expected was [%Expcod%]
Pause
Echo.
GoTo :EOF
Args.ps1:
Set-StrictMode -Version 3.0 # disallow implicit variables
function ArgNonEmtFstGet( $arrArgs ) { # get the first non-empty argument
ForEach( $strArgs In $arrArgs ) { # <=== why is this double-indirection needed?
ForEach( $strArg In $strArgs ) { # scan all argument strings
$intArgLen = $strArg.Length # get length of current argument-string
If( $intArgLen -gt 0 ) { Return $strArg } # first time length is greater than zero,quit scanning and pass that argument back to caller
} # done scanning
} # <=== why is this double-indirection needed?
"" # when we finished scanning and did not return early,return a non-crashing value
} # ArgNonEmtFstGet
# Step 0: show we are alive
"Arg count: " + $args.Length # emit how many strings exist that *Could be* arguments
# Step 1: look for any argument at all
If( $args.Length -eq 0 ) { # when there are zero strings that look like arguments
Write-Output "Putative argument required." # emit error message for 'no strings found'
Exit( 1 ) # abort with exit code
} # done with no strings found that *Could be* arguments
"First putative argument: [" + $args[0] + "]" # emit the first argument-looking string
# Step 2: look for an argument that is actually meaningful
$strArg = ArgNonEmtFstGet( $args ) # get the first non-empty argument; that is our input
If( $strArg -eq "" ) { # when it is not a usable argument
Write-Output "Non-empty argument required." # emit error message for 'no real arguments found'
Exit( 2 ) # abort with exit code
} # done with no usable arguments found
"First non-empty argument: [$strArg]" # emit the first valid argument; this is our input
解决方法
我无法使用嵌套的 foreach 语句重现您的问题。 当我删除内部循环时,我得到了相同的结果。
但我建议您应该避免使用 $args 变量,因为它接受任何输入并且不允许命名绑定(仅限位置)。
只需在您的 PowerShell 脚本中声明一个 param 块,如下所示,然后您就不需要遍历您的参数,此外,您已经命名了可以在批处理脚本中使用的参数。 (位置绑定仍然是可能的)。
带有 param 块和“ValidateScript”属性的示例脚本,用于验证输入以避免空输入、空输入或空白输入。
param(
# first mandatory parameter. Default Position = 1 (if using positional binding)
[Parameter(Mandatory=$true)]
[ValidateScript(
{
if([String]::IsNullOrWhiteSpace($_)) {
throw [System.ArgumentException]'Your string is null,empty or whitespace!'
} else {
$true
}
}
)]
[string]
$varA,# second mandatory parameter. Default Position = 2 (if using positional binding)
[Parameter(Mandatory=$true)]
[ValidateScript(
{
if([String]::IsNullOrWhiteSpace($_)) {
throw [System.ArgumentException]'Your string is null,empty or whitespace!'
} else {
$true
}
}
)]
[string]
$varB,# You can just use this parameter if you want your arguments being absolutely dynamic
[Parameter(ValueFromRemainingArguments)]
[ValidateScript(
{
if([String]::IsNullOrWhiteSpace($_)) {
throw [System.ArgumentException]'Your string is null,empty or whitespace!'
} else {
$true
}
}
)]
[string[]]
$remainingArgs
)
# create ordered dictionary of mandatory arguments used for returning object
$properties= [ordered]@{
'Arg0' = "'$varA'"
'Arg1' = "'$varB'"
}
# add any optional remaining argument (if any)
$index = 1 # start index
foreach ($remainingArg in $remainingArgs) {
$index++
$properties.Add("Arg$index","'$remainingArg'")
}
# return as object
[pscustomobject]$properties
如脚本中所述,如果您希望脚本在参数方面绝对动态,您也可以仅使用第三个参数。
关于属性“ValidateScript”的更多信息:https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions_advanced_parameters?view=powershell-5.1#validatescript-validation-attribute
附注: 批量使用命名参数时的示例命令:
PowerShell.EXE -F %Script% -varA "%3" -varB "%4"
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。