函数中未使用参数的默认值

lvt*_*llo 1 powershell parameter-passing

我有一个非常基本的PowerShell脚本:

Param(
    [string]$MyWord
)

function myfunc([string] $MyWord) {
    Write-Host "$MyWord"
}
myfunc @PSBoundParameters
Run Code Online (Sandbox Code Playgroud)

这是我的执行方式:

PS C:\>。\ test.ps1 -MyWord'hello'
你好

一切都很好。但是,如果-MyWord未指定,我想设置一个默认值。我尝试了这个:

Param(
    [string]$MyWord='hi'
)

function myfunc([string] $MyWord) {
    Write-Host "$MyWord"
}
myfunc @PSBoundParameters 
Run Code Online (Sandbox Code Playgroud)

但是比我的脚本的输出还空。当我不描述参数时,它什么也没打印。(如果我指定了参数,它只会显示“ hello”)。我也尝试过:

Param(
    [string]$MyWord
)

function myfunc([string] $MyWord) {
    [string]$MyWord='hi'
    Write-Host "$MyWord" 
}
myfunc @PSBoundParameters
Run Code Online (Sandbox Code Playgroud)

但是,当然,输出总是“ hi”而不是“ hello”。即使使用参数执行脚本-MyWord 'hello'

有人可以解释我在做什么吗?

当我不使用该功能时,它按预期工作:

Param(
    [string]$MyWord='hi'
)
Write-Host $MyWord
Run Code Online (Sandbox Code Playgroud)

输出:

PS C:\>。\ test.ps1 -MyWord'你好'
哈罗

PS C:\>。\ test.ps1
你好

mkl*_*nt0 5

$PSBoundParameters顾名思义,自动变量仅包含绑定参数,其中bound表示调用者提供了实际值

因此,一个参数的默认值,没有资格作为结合相关的参数,所以$MyWord用它的默认值'hi'不能成为的一部分$PSBoundParameters

注意:可以说,具有默认值的参数也应视为已绑定(由其默认值绑定,而不是由调用方提供的值绑定)。无论哪种方式,都有一个也包含默认值的自动变量会很方便,以便能够简单,全面地传递参数。建议已提交到此处的PowerShell GitHub存储库。


解决方法

  • 下面的解决方案,假设你想传递的默认值通过,并且不希望简单地复制在功能的默认值myfunc(如证明安斯加尔Wiecher的有用的答案),因为这将创建一个维护的负担。

  • 关于函数语法:以下两种形式是等效的,尽管出于一致性和可读性的考虑,您可能更喜欢后者。

    • function myfunc([string] $MyWord = 'hi') { ... }
      (...)函数名后面的 参数声明。
    • function myfunc { param([string] $MyWord = 'hi') ... }
      在函数体内的 一个param(...)内的参数声明。

一个简单的解决方法是将默认值显式添加到$PSBoundParameters

Param(
     [string]$MyWord = 'hi'
)

function myfunc ([string] $MyWord){
    Write-Host "$MyWord" 
}

# Add the $MyWord default value to PSBoundParameters.
# If $MyWord was actually bound, this is effectively a no-op.
$PSBoundParameters.MyWord = $MyWord

myfunc @PSBoundParameters
Run Code Online (Sandbox Code Playgroud)

为了一般地实现您想要的,您必须使用反射(自省):

Param(
     [alias('foop')]
     [string]$MyWord = 'hi'
)

function myfunc ([string] $MyWord){
    Write-Host "$MyWord" 
}

# Add all unbound parameters that have default values.
$boundAndDefaultValueParams = $PSBoundParameters
foreach($paramName in $MyInvocation.MyCommand.Parameters.Keys) {
  if (-not $boundAndDefaultValueParams.ContainsKey($paramName)) {
    $val = Get-Variable $paramName -ValueOnly
    if ($null -ne $val) { $boundAndDefaultValueParams.Add($paramName, $val) }
  }
}

myfunc @boundAndDefaultValueParams
Run Code Online (Sandbox Code Playgroud)