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

如何将具有 3 个值的数组传递给ArrayCount函数?为什么某些调用的计数为零?

4

2 回答 2

6

在 PowerShell 中,$args是一个引用未命名参数的自动变量。只需更改您的参数名称:

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

你会得到预期的输出

1
3
3
于 2019-11-12T12:03:00.837 回答
1

编写于:PowerShell Core 7.0.0-preview.5 / Windows PowerShell v5.1

为了补充Martin Brandl 的有用、中肯的回答,并提供一些背景信息:

$args自动变量的一个实例,表示其值由 PowerShell 本身控制的特殊变量。

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

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

相反,现在正在通过PSScriptAnalyzer发出关于在安装了PowerShell 扩展的Visual Studio Code中分配给自动变量的设计时警告。

不幸的是,如果没有 Visual Code 的帮助,您通常无法从变量名中推断出变量是否指代自动变量,并且也没有发现它们的编程方式——阅读帮助主题是唯一的选择;见下一节。


应该写但事实上的自动变量列表是:

笔记:

  • 该列表是使用以下命令生成的,从Windows PowerShell v5.1 开始 - 在 PowerShell Core中,该列表更短,因为删除了一些过时的变量。

    • 该命令依赖于相关帮助主题的字符串解析;这种方法很脆弱,但它是目前发现自动变量的唯一方法,因为没有基于反射的方法。

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

  • 随着时间的推移,可以引入新的自动变量;如果合适的话,希望它们的只读状态将在引入时强制执行。

    • 因为自动变量与用户变量共享一个命名空间,所以引入任何新的自动变量都有破坏现有代码的风险。
  • 一些自动变量是合法可写的:

    • $OFS- 字符串化数组时使用的分隔符。
    • $null- 特殊变量不仅在读取时表示空值,而且还设计为允许为其分配任何值以丢弃它(而不是将其写入(成功)输出流)。
    • $LASTEXITCODE,在表单$global:LASTEXITCODE中,设置要使用的进程退出代码(尽管最好使用exit <n>)。
  • 下面未列出的自动变量(例如, $PID)实际上已经只读的(它们应该是)。下面列出的那些 - 即意外可写的 - 属于以下类别

    • 那些总是悄悄地丢弃分配的值(例如$Input,,,$MyInvocation$PSCmdlet

    • 那些接受自定义值,但在自动分配值的上下文中覆盖(隐藏)它的那些(例如,$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

“预定义”是指它们是否默认存在于全局范围内。

以下代码用于检测它们 - 您可以$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.

于 2019-11-12T14:33:30.597 回答