【问题标题】:Require and test for named arguments only仅需要和测试命名参数
【发布时间】:2019-03-08 03:02:30
【问题描述】:

我有一个旨在接受命名参数的脚本,我想在未命名或错误命名参数的情况下提供一些错误检查,但我发现有些奇怪。

脚本以参数块开头,像这样

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 不会被填充,并且未指定位置的参数仍然可以是位置参数,并且只需使用 (Position=#)是否允许将该位置定义为与参数本身定义的顺序不同?

【问题讨论】:

  • 不完全是重复的,因为该链接都是关于将命名参数和值放入哈希表中,而我的问题是关于处理和/或阻止未命名参数。无论如何,PowerShell 似乎仍然无法强制命名参数,我将解决这个事实。

标签: powershell


【解决方案1】:

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

[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)]
  • 默认情况下,所有参数都是位置
  • CmdletBinding 属性的 PositionalBinding 参数 允许您控制默认设置。如果您不使用 论点,那么您有效地将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 字符:1 + namedArguments -a “测试” -b 20 -c 43 “额外参数” + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~ + CategoryInfo : InvalidArgument: (:) [namedArguments], ParameterBindingException + FullyQualifiedErrorId : PositionalParameterNotFound,namedArguments

namedArguments "no parameter name"

namedArguments : 找不到接受的位置参数 参数'没有参数名称'。在行:1 字符: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)] 属性 您的参数以获得类似的效果,即要求所有 参数被命名。但是,如果您只有一个参数 函数定义,这不会产生预期的结果,并且 将允许唯一的未命名参数。

.

【讨论】:

  • 这让我更接近了,但我没有看到如何使用 $PSBoundParameters 来识别未命名的参数。即使我只传递未命名的参数,我仍然会在 $PSBoundParameters 中获得值。垃圾值,无法识别它们。我在这里错过了什么?
  • 啊,我猜如果$PSBoundParameters有一个key,extraParameters,那么我知道所有未命名的参数,否则我需要查看extraParameters的值来确定我是否有双层参数或未命名。变得相当复杂。
  • 我似乎记得在某处读到使用 CmdletBinding 会破坏返回代码,并且我还需要此脚本将信息返回给调用作业。看起来我最好先测试一下,然后再花太多时间实施这种方法。
  • 我添加了更多细节以了解如何使用$PSBoundParamters 访问未命名的参数。同样的逻辑也可以用于检查命名参数。
【解决方案2】:

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                                            

【讨论】:

    猜你喜欢
    • 2016-06-01
    • 2012-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多