仅要求和测试命名参数

时间:2019-03-07 10:54:28

标签: powershell

我有一个旨在接收命名参数的脚本,并且在未命名或命名错误的情况下,我想提供一些错误检查,并且我觉得有些奇怪。

脚本以这样的参数块开头

param (
    [string][Alias("s")]  $sets,
    [string][Alias("l")]  $location, # /MLF #
    [switch][Alias("c")]  $conform,

    [string][Alias("eM")] $exitMode,
    [string][Alias("iM")] $interactionMode,
    [string][Alias("lM")] $logMode,
    [switch][Alias("tM")] $testMode,

    [parameter(ValueFromRemainingArguments = $true)][object[]]$extraParameters = @()
)

然后我可以使用它来获取无关参数数据的数组并去除值,因此可以为用户提供无关参数或命名错误的列表。一切都很好。

if ($extraparameters.count -gt 0) {
    $invalidArguments = New-Object Collections.ArrayList
    foreach ($namedParameter in $extraParameters) {
        if ($namedParameter.StartsWith('-')) {
            $invalidArguments.Add($namedParameter) > $null
        }
    }
    Write-PxLog "{initError-[cf][2]}_Invalid arguments '$($invalidArguments -join ', ')'"
    $proceed = $false
}

稍后,我还将验证某些参数的值,例如在此处,如果未提供日志模式,则将提供默认日志模式;在未提供日志模式的情况下,将验证提供的日志模式;并记录错误。 。一切正常。

if ($logMode) {
    if ($validLogMode = Resolve-PxLogMode $logMode) {
        $logMode = $validLogMode
    } else {
        Write-PxLog "{initError-[cf][2]}_Invalid -logMode '$($logMode)'"
        $logMode = $null
        $proceed = $false
    }
} else {
    $logMode = 'Terse'
}

所有这些都带有预期的命名参数,因此命令行可能是

sets:Proxy -logMode:Verbose -eM:exitConsole

一切都很好。

但是,当我尝试针对未命名的参数进行验证时,一切都崩溃了。如果我在命令行上使用

Proxy exitConsol verbose

我希望$args.count为3,我希望exitMode,interactionMode和logMode为默认值。但是$args.count为0,甚至更奇怪,exitMode也会针对“详细”进行验证。我的假设是Conform参数,它是一个开关,将其抛出,因此$ exitMode接受提供的第三个字符串。

那么,如何强制命名参数并测试未命名的参数,以便提供有意义的错误?我希望$ args可以解决问题,但是似乎如果您使用Param()块,则不会填充$ args,并且未指定位置的参数仍可以是位置参数,并使用{{1} }只是允许将该位置定义为与参数本身的定义顺序不同?

2 个答案:

答案 0 :(得分:1)

要要求输入除$extraParameters以外的所有参数值及其名称,只需在[CmdletBinding(PositionalBinding=$false)]关键字之前添加属性param

[CmdletBinding(PositionalBinding=$false)]
param (
    [string][Alias("s")]  $sets,
    [string][Alias("l")]  $location, # /MLF #
    [switch][Alias("c")]  $conform,

    [string][Alias("eM")] $exitMode,
    [string][Alias("iM")] $interactionMode,
    [string][Alias("lM")] $logMode,
    [switch][Alias("tM")] $testMode,

    [parameter(ValueFromRemainingArguments = $true)][object[]]$extraParameters = @()
)

要访问传递给函数或命令的参数,应在代码内部使用以下内容:

$PSBoundParameters

说明

[CmdletBinding(PositionalBinding=$false)]
  • 默认情况下,所有参数均为位置
  • PositionalBinding属性的CmdletBinding自变量 允许您控制默认设置。如果您不使用 参数,那么您已经有效地将PositionalBinding设置为 $true。保留值$true将允许未命名的参数 传递给您的命令。

$PSBoundParameters

  • $PSBoundParameters包含一个传递给您的命令或脚本的参数字典。只能在声明参数的范围内访问它。
  • 变量或字典将列出使用param关键字声明的参数。
  • 可以使用命名索引访问参数和参数值。如果已声明名为-computer的参数,则可以从$PSBoundParameters['Computer']在函数中访问该值。
  • 要访问代码中未命名的参数值,只需使用$PSBoundParameters['extraParameters']
  • 此处可以使用[PSBoundParametersDictionary]对象的方法和属性。

$args

  • 包含未声明参数的值的数组,即不使用param关键字。

ValueFromRemainingArguments = $true

  • 将此参数设置为true允许将未命名的参数值传递到命令中。会将值分配给具有此参数的参数,而忽略PositionalBinding的值。

有关其他说明和可能性,请参见about_Functions_Advanced_Parametersabout_Automatic_Variables

使用命名参数的示例

function namedArguments {
[cmdletbinding(PositionalBinding=$false)]
param(
[parameter()][string]$a,
[parameter()][byte]$b,
[parameter()][byte]$c

)

write-host "args is "
$args
write-host "parameters is "
$psboundparameters

}

namedArguments -a "hi" -b 2 -c 3
args is
parameters is

Key Value
--- -----
a   hi
b   2
c   3

使用未命名的参数值调用上述函数:

namedArguments -a "test" -b 20 -c 43 "extraparameter"
  

namedArguments:找不到可以接受的位置参数   参数“额外参数”。在第1行:char:1   + namedArguments -a“测试” -b 20 -c 43“ extraparameter”   + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~       + CategoryInfo:InvalidArgument:(:) [namedArguments],ParameterBindingException       + FullyQualifiedErrorId:PositionalParameterNotFound,namedArguments

namedArguments "no parameter name"
  

namedArguments:找不到可以接受的位置参数   参数“无参数名称”。在第1行:char:1   + namedArguments“没有参数名称”   + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~       + CategoryInfo:InvalidArgument:(:) [namedArguments],ParameterBindingException       + FullyQualifiedErrorId:PositionalParameterNotFound,namedArguments

使用命名和未命名示例

function AllArgumentsWelcome {
[cmdletbinding(PositionalBinding=$false)]
param(
[parameter()][string]$a,
[parameter()][byte]$b,
[parameter()][byte]$c,
[parameter(ValueFromRemainingArguments = $true)][string]$d
)

$PSBoundParameters
 if ($PSBoundParameters['d']) { # Checking unnamed parameters
  "$($PSBoundParameters['d']) was not assigned to a named parameter!"
 }
}

AllArgumentsWelcome -a "test" -b 2 -c 3 "no parameter name"

Key Value
--- -----
a   test
b   2
c   3
d   no parameter name
no parameter name was not assigned to a named parameter!
  • 请注意,上面的示例访问传递给函数的未命名参数值。

注意事项

  • 可以为所有设置[Parameter(Position=0)]属性 的参数以获得与要求所有参数相似的效果 参数被命名。但是,如果您在一个 函数定义,这将不会产生预期的结果,并且 唯一的未命名参数将被允许。

答案 1 :(得分:0)

AdminOfThings' answerFrode F.answer to Get ValueFromRemainingArguments as an hashtable放在一起:

[CmdletBinding(PositionalBinding=$false)]
param (
    [string][Alias("s")]  $sets,
    [string][Alias("l")]  $location, # /MLF #
    [switch][Alias("c")]  $conform,

    [string][Alias("eM")] $exitMode,
    [string][Alias("iM")] $interactionMode,
    [string][Alias("lM")] $logMode,
    [switch][Alias("tM")] $testMode,

    [parameter(ValueFromRemainingArguments)][object[]]$extraParameters = @()
)
    #Convert extraparameters to an OrderedDictionary (initially hashtable)
    $htvars = [ordered]@{}
    $iii = 0                   # extraparameters counter
    $lastvar = "unnamed_$iii"
    $extraparameters | ForEach-Object {
        $iii += 1 
        if($_ -match '^-') {
            #New parameter
            $lastvar = $_ -replace '^-'
            $htvars[$lastvar] = $null
        } else {
            #Value
            $htvars[$lastvar] = $_
            $lastvar = "unnamed_$iii" 
        }
    }
    #Propagate OrderedDictionary
    $htvars

上述解决方案支持以下参数:

  • 简单值(单个项目)
  • 数组值
  • 空值(开关)
  • 未命名的值

示例输出:

PS D:\PShell> .\SO\55042084a.ps1 -a111 -s 555 -abc "ABC" -num 15, 17 'ghi' -Seria "seria" "fifi" -l "llllů" -foo

Name                           Value                                           
----                           -----                                           
a111                                                                           
abc                            ABC                                             
num                            {15, 17}                                        
unnamed_5                      ghi                                             
Seria                          seria                                           
unnamed_8                      fifi                                            
foo                                                                            

PS D:\PShell> .\SO\55042084a.ps1 a111 -s 555 -abc "ABC" -num 15, 17 'ghi' -foo -Seria "seria" "fifi" -l "llllů"

Name                           Value                                           
----                           -----                                           
unnamed_0                      a111                                            
abc                            ABC                                             
num                            {15, 17}                                        
unnamed_5                      ghi                                             
foo                                                                            
Seria                          seria                                           
unnamed_9                      fifi