在 Powershell 中,如何对参数语句中未列出的无效标志和开关生成错误?

Bil*_*ore 2 parameters powershell parameter-passing

试图让 get param(...) 进行一些基本的错误检查...令我困惑的一件事是如何检测不在参数列表中的无效开关和标志?

    function abc {
        param(
            [switch]$one,
            [switch]$two
        )
    }
Run Code Online (Sandbox Code Playgroud)

当我使用它时:

PS> abc -One -Two
# ok... i like this

PS> abc -One -Two -NotAValidSwitch
# No Error here for -NotAValidSwitch?  How to make it have an error for invalid switches?
Run Code Online (Sandbox Code Playgroud)

mkl*_*nt0 5

正如Santiago SquarzonAbraham Zinalazett42在评论中指出的那样,您所需要做的就是使您的函数(或脚本)成为高级(类似 cmdlet)函数

  • 显式地,通过param(...)[CmdletBinding()]属性装饰块。

  • 和/或隐式地,通过用属性修饰至少一个参数变量[Parameter()]

   function abc {
        [CmdletBinding()] # Make function an advanced one.
        param(
            [switch]$one,
            [switch]$two
        )
   }
Run Code Online (Sandbox Code Playgroud)

高级函数自动确保只能传递绑定到显式声明的参数的实参
如果传递了意外的参数,调用将失败并出现语句终止错误。

切换到高级脚本/函数有副作用,但主要是有益的

  • 您将获得对常用参数的自动支持,例如-OutVariable-Verbose

  • 您失去了通过自动变量接收未绑定参数的能力(这是这里所需要的);但是,您可以通过以下方式为任何剩余的位置参数声明一个包罗万象的参数$args[Parameter(ValueFromRemainingArguments)]

  • 要在高级函数或脚本中接受管道输入[Parameter(ValueFromPipeline)],必须通过(整个对象)或[Parameter(ValueFromPipelineByPropertyName)](与参数名称匹配的输入对象的属性值)属性将参数显式声明为管道绑定。

有关简单(非高级)和高级功能以及二进制cmdlet 的并置,请参阅此答案


如果您不想使您的功能成为高级功能:

检查自动$args变量(反映任何未绑定参数(更简单的替代方案$MyInvocation.UnboundArguments))是否为空(空数组),如果不是,则抛出错误:

   function abc {
        param(
            [switch]$one,
            [switch]$two
        )
        if ($args.Count) { throw "Unexpected arguments passed: $args" }
   }
Run Code Online (Sandbox Code Playgroud)

保持函数简单高级)的潜在原因:

  • 在参数声明中“减少仪式”,例如仅通过自动$input变量进行管道输入处理。

  • 通常,对于简单的辅助函数,例如不需要公共参数支持的模块或脚本内部使用。

  • 当函数充当外部程序的包装器时,要向其传递参数,并且其参数(选项)与 PowerShell 公共参数的名称和别名(例如-verbose-ov( -Out-Variable))冲突。

什么不好的理由:

  • 当您的函数从模块导出并且具有不规则名称(不遵守<Verb>-<Noun>基于批准的动词的 PowerShell 命名约定)并且您希望避免在导入该模块时发出警告时。

  • 首先,这不是简单函数与高级函数的问题,而是仅与从模块导出函数有关;也就是说,即使是一个不规则命名的简单函数也会触发警告。这个警告的存在是有充分理由的:从模块导出的函数通常是“公共的”,即(也)供其他用户使用,他们有理由期望命令名称遵循 PowerShell 的命名约定,这极大地促进了命令发现。同样,用户会期望模块导出的函数具有类似于 cmdlet 的行为,因此最好仅导出高级函数。

  • 如果您仍然想使用不规则名称同时避免出现警告,您有两种选择:

    • 完全忽略命名约定(不建议)并选择不包含-字符的名称,例如doStuff- PowerShell 将不会发出警告。更好的选择是选择一个常规名称并将不规则名称定义为它的别名(见下文),但请注意,即使是别名也有一个(不太严格遵守的)命名约定,基于官方的一两个字母为每个批准的动词定义的前缀,例如gforGet-safor Start-(请参阅上面的批准动词文档链接)。

    • 如果您确实想使用<Verb>-<Noun>约定,但使用未经批准的动词(之前的标记),请使用常规名称(使用批准的动词)-定义函数,并为其定义和导出使用不规则名称的别名(别名不是受到警告)。例如,如果您想要一个名为 的命令,请将函数命名为,然后定义。请注意,这两个命令都需要导出,因此对导入者可见。Ensure-FooSet-Foo
      Set-Alias Ensure-Foo Set-Foo

  • 最后,请注意,警告也可以在 import 上被抑制,即 via 。这种方法的缺点 - 除了给导入器增加消除警告的负担之外 - 是模块导出的自定义类不能以这种方式导入,因为导入此类类需要一个语句,该语句没有消除选项(如PowerShell 7.2.1 的版本;有关背景信息,请参阅GitHub 问题 #2449Import-Module -DisableNameCheckingusing module