【问题标题】:powershell function switch param with string array带有字符串数组的powershell函数开关参数
【发布时间】:2020-05-31 07:58:49
【问题描述】:

我很难理解以下函数的输出

function testApp
{
    param(
        [string] $appName,
        [switch] $sw = $false,
        [string[]] $test,
        [string[]] $test2
    )

    Write-Host $appName - $sw - $test - $test2
}
  1. testApp -appName "TestApp" -sw $true -test "one", "two" -test2 "three","four"

输出:TestApp - True - 一二 - 三四

  1. testApp -appName "TestApp" -sw $true -test "one", "two"

输出:TestApp - True - 一二 - True

第一个输出符合预期。但是当我没有通过它时,我无法理解为什么 test2 数组的第二个输出为“True”。谁能帮我理解这种行为的原因?谢谢。

【问题讨论】:

  • 请格式化您的代码。现在它几乎不可读。操作方法链接在您创建问题的页面上。
  • 出现True 的原因是您误用了[switch] 参数。 [grin] 当您添加 -sw $True 时,您将 sw 翻转为 TRUE [默认情况下为 FALSE] 并且您发送的值未分配给任何内容。由于在第二个示例中您没有直接为 $Test2 分配任何内容,因此剩余的 $True 被传递给剩余的参数 - -Test2 参数。
  • 是的,当使用 [switch] 时,您没有给它一个默认值。如果在调用中指定,它将测试 $true,如果未指定,它将测试 $false。当我第一次编写函数时,我很难理解这一点。
  • 要添加到其他 cmets,如果您想为开关参数指定一个值,您可以这样做:“-sw:$true” - 即“连字符开关名称 冒号 值”。此语法不同于使用空格分隔参数名称和值的非开关参数,如果您的开关值来自变量 - 例如“-sw:$myVar”。

标签: powershell


【解决方案1】:

总结和补充Lee_DaileyMatthewmclayton 对问题有帮助的cmets:

[switch] PowerShell 中的参数(在其他 shell 中也称为 flags):

switch 参数旨在暗示 $true$false 在调用中存在:例如,传递-sw 自身 表示$true,而省略 -sw 表示$false

  • 可以传递一个布尔值显式,目的是传递一个以编程方式确定的值;例如:-sw:$var

    • 注意开关名称后面的:,它告诉PowerShell布尔值属于开关参数;没有它,PowerShell 认为该值是 positional 参数,用于 不同 参数(见下文)。

    • 警告:命令可能解释-sw:$false省略-sw不同;一个突出的例子是使用公共参数-Confirm:$false 覆盖有效的$ConfirmPreference 值。

      • 如果您需要在自己的代码中进行这种区分,请使用$PSBoundParameters.ContainsKey('sw') -and -not $sw 来检测-sw:$false 的情况。
  • 不要为开关参数变量分配默认值:虽然技术上可行,约定是开关默认为@ 987654343@(这是[switch] 实例的默认值);也就是说,[switch] 参数应始终具有 opt-in 逻辑。

[switch] 参数变量在大多数情况下实际上表现得像 布尔

  • 也就是说,例如,你可以说if ($sw) { ... }

  • 如果您需要显式访问包装的布尔值,请访问 .IsPresent 属性(请注意,属性名称有些混乱,因为在 -sw:$false 调用中,开关仍然是 存在,但它的,反映在.IsPresent中,是$false)。

    • 需要.IsPresent 的一个示例是使用布尔值作为隐式数组索引,特别是模拟三元条件[1]('falseValue', 'trueValue')[$sw.IsPresent];如果没有.IsPresent,则无法识别有效的布尔值,也不会自动映射到索引0(来自$false)或1(来自$true)。

最终,您的问题是您认为$true-sw 的参数,而它变成了一个位置 参数隐式绑定到@ 987654361@参数.

[switch] 参数从不需要一个值,因此下一个参数成为一个单独的位置参数 - 除非您通过以下方式明确指出该参数属于开关开关名称为:,如上图。[2]

Positional vs. named 参数在 PowerShell 中传递:

术语说明:为了概念清晰,术语参数用于指代传递给已声明参数。这避免了在情境中使用 parameter 来指代 接收值的语言结构给定值 的歧义。

  • 命名参数传递(绑定)是指在参数之前显式放置目标参数名称(通常用空格分隔,但也可以使用和/或@987654364 @);例如,-AppName foo

    • 传递命名参数的顺序无关紧要
  • 位置(未命名)参数传递是指传递一个参数而不在其前面加上其目标参数的名称;例如,foo

    • 在其他未命名参数之间的相对位置(顺序)决定了隐含的目标参数的意义上,传递是位置性的。
  • [switch] 参数是例外,因为它们:

    • 通常由 name only (-sw) 传递,暗示值 $true,如果传递了值 require : 将名称与值分开。
    • 从不支持位置绑定。
  • 您可以结合命名传递与位置传递,在这种情况下,命名参数首先绑定,然后再考虑位置参数(按顺序) 用于绑定到尚未绑定的参数。

  • PowerShell 函数默认是简单 函数。 为了对位置绑定进行控制,必须使用[CmdletBinding()] 和/或[Parameter()] 属性(见下文),它们总是将一个简单的函数变成一个高级功能

    • 将一个简单的功能变成高级功能会产生更大的行为影响(主要是有益的),详见this answer
  • 默认情况下,PowerShell 函数接受 任何 参数 的位置参数([switch] 类型除外),参数已声明

    • 此外,简单函数接受任意附加参数没有声明参数,这些参数收集在自动$args数组变量中.
  • 为防止您的函数接受任何位置参数默认情况下,请在上方放置 [CmdletBinding(PositionalBinding=$false, ...)] 属性param(...) 块。

      1234563
  • 顺便说一句:当您实现 cmdlet(以二进制 实现的命令,通常通过 C#)时,此行为是隐含的.

  • 为了有选择地支持位置参数,使用 [Parameter(Position=<n>, ...)] 属性(例如,[Parameter(Position=0)] [string] $Path)装饰各个参数声明

      1234563建议将0 作为自记录约定,因为它是明确的。
  • 属性[Parameter(Position=<n>)] 是一个明确的选择,选择性地覆盖[CmdletBinding(PositionalBinding=$false)]:也就是说,后者禁用位置绑定除非个人明确指出参数声明;事实上,后者是由前者隐含,在那个一旦你使用one[Parameter(Position=<n>)]属性,你必须在你想要的所有其他参数上使用它也可以按位置绑定


  • [1] 请注意,PowerShell [Core] 7.0+ 原生支持三元条件:$sw ? 'trueValue' : 'falseValue'

    [2] 实际上,[switch] 参数是 PowerShell 支持 可选 参数的唯一类型。请参阅this answer 了解更多信息。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-02-03
      • 1970-01-01
      • 2012-09-23
      • 2019-07-02
      • 2020-05-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多