将参数作为数组传递给PowerShell函数

3

我正在尝试弄清楚如何将多个字符串作为数组传递给Powershell函数。

function ArrayCount([string[]] $args) {
    Write-Host $args.Count
}

ArrayCount "1" "2" "3"
ArrayCount "1","2","3"
ArrayCount @("1","2","3")

打印

2
0
0

如何将三个值的数组传递给 ArrayCount 函数? 一些调用为什么计数为零?
2个回答

7
在PowerShell中,$args是一个自动变量,它指的是未命名的参数。只需更改您的参数名称即可:
function ArrayCount([string[]] $myParam) {
    Write-Host $myParam.Count
}

你将获得预期的输出

1
3
3

1

撰写时间:PowerShell Core 7.0.0-preview.5 / Windows PowerShell v5.1

为了补充Martin Brandl的简明易懂的答案,以下是一些背景信息:

$args是一个自动变量的实例,这意味着它是一个特殊的变量,其值由PowerShell自身控制

因此,您应该避免自定义使用自动变量,即使在它偶然起作用的情况下(见下文)。

理想情况下,PowerShell本身会一致地防止这种自定义使用自动变量(它只对一些自动变量进行这样的操作),并且在GitHub上有辩论关于强制执行此操作,但最终出于向后兼容性的原因而决定不这样做。

现在,如果安装了PowerShell扩展程序,则在Visual Studio Code中关于自动变量的赋值会发出PSScriptAnalyzer设计时警告

不幸的是,如果没有Visual Code的帮助,您通常无法从变量名称中推断出它是否是自动变量,也没有编程方式可以发现它们 - 阅读帮助主题是唯一的选择;请参见下一部分。


不应该被写入的自动变量列表,但实际上是可写的:

注意:

以下是生成列表的命令,截至Windows PowerShell v5.1 - 在PowerShell Core中,该列表较短,因为一些过时的变量已被删除。
  • 该命令依赖于相关的帮助主题字符串解析;这种方法很脆弱,但目前是发现自动变量的唯一方法,因为没有基于反射的方法。

  • 此GitHub问题建议实现编程可发现性,并讨论引入一个单独的、保留的命名空间来存放自动变量的可能性。

随着时间的推移,可能会引入新的自动变量;希望在引入时能够强制执行它们的只读状态(如果适用)。
由于自动变量与用户变量共享命名空间,引入任何新的自动变量都存在破坏现有代码的风险。 一些特定的自动变量是合法可写的
  • $OFS - 字符串化数组时要使用的分隔符。
  • $null - 特殊变量,在读取时不仅表示空值,而且设计为允许将任何值分配给它以丢弃它(不将其写入(成功)输出流)。
  • $LASTEXITCODE,形式为$global:LASTEXITCODE,用于设置要使用的进程退出代码(虽然最好使用exit <n>来完成)。
列在下面的自动变量(例如$PID)已经有效地是只读的(正如它们应该是的)。下面列出的那些 - 即意外可写的变量 - 属于以下类别
  • 始终静默丢弃分配的值的变量(例如$MyInvocation

  • 接受自定义值,但在自动分配值的上下文中覆盖(隐藏)它的变量(例如$args,每当进入新的脚本块(函数/脚本)时,它都会自动设置为一个局部变量;您作为局部参数变量的使用实际上被忽略了)。

  • 混合案例,特别是$_ / $PSItem,在任何上下文中分配的值都会被静默丢弃,除非在此类上下文中$_自动分配一个值,但在这种情况下,您可以(但不应该)分配一个新值(例如,'in' | ForEach-Object { $_ = $_ + '!'; $_ }输出in!)。

Name                          Predefined
----                          ----------
_                                  False
AllNodes                           False
Args                                True
Event                              False
EventArgs                          False
EventSubscriber                    False
ForEach                             True
Input                               True
Matches                             True
MyInvocation                        True
NestedPromptLevel                   True
Profile                             True
PSBoundParameters                   True
PsCmdlet                           False
PSCommandPath                       True
PSDebugContext                     False
PSItem                             False
PSScriptRoot                        True
PSSenderInfo                       False
Pwd                                 True
ReportErrorShowExceptionClass      False
ReportErrorShowInnerException      False
ReportErrorShowSource              False
ReportErrorShowStackTrace          False
Sender                             False
StackTrace                          True
This                               False

"Predefined" 指的是它们是否默认存在于全局范围内。
以下代码用于检测它们 - 您可以在此之前设置 $VerbosePreference = 'Continue'(并在之后重置)以查看已经只读的变量:
$autoVarNames = ((get-help about_automatic_variables) -split [environment]::newline -match '^\s*\$\w+\s*$').Trim().Trim('$') | Sort-Object -Unique

foreach ($varName in $autoVarNames) {
  $var = Get-Variable $varName -ErrorAction 'SilentlyContinue'
  $exists = $?
  if ($exists -and $var.Options -match 'readonly|constant') {
    Write-Verbose "$varName`: read-only or constant"
  } elseif ($varName -in 'NULL', 'OFS', 'LastExitCode') { # exceptions
    Write-Verbose "$varName`: writable by design"
  } else {
    Set-Variable -Name $varName -Value $null -ErrorAction SilentlyContinue
    if ($?) {
      [pscustomobject] @{ Name = $varName; Predefined = $exists }
    }
  }
}

请注意,代码中有一个硬编码的异常列表,以避免报告应该是可写的自动变量,例如$OFS$LastExitCode,或者可以作为安静的无操作符分配的$null

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接