Passing arguments as array to PowerShell function

﹥>﹥吖頭↗ 提交于 2021-02-08 03:07:26

问题


I'm trying to figure out how I can pass multiple strings as an array to a powershell function.

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

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

Prints

2
0
0

How can I pass an array with 3 values to the ArrayCount function? Why is the Count zero for some of the invocations?


回答1:


In PowerShell, $args is a automatic variable that refers to unnamed arguments. Just change your parameter name:

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

And you will get the expected output:

1
3
3



回答2:


Written as of: PowerShell Core 7.0.0-preview.5 / Windows PowerShell v5.1

To complement Martin Brandl's helpful, to-the-point answer with some background information:

$args is an instance of an automatic variable, meaning a special variable whose value is controlled by PowerShell itself.

Therefore, you should avoid custom use of automatic variables, even in cases where it happens to work (see below).

Ideally, PowerShell would itself prevent such custom use consistently (it only does so for some automatic variables), and there was debate on GitHub about enforcing that, but it was ultimately decided not to do it for reasons of backward compatibility.

Instead, the plan is to emit design-time warnings about assignments to automatic variables in Visual Studio Code with the PowerShell extension installed, via PSScriptAnalyzer.

As of PowerShell extension version 2019.11.0 (based on the PSScriptAnalyzer version it comes bundled with), this is only partially implemented: While there is an available PSAvoidAssignmentToAutomaticVariable rule, it only warns about assigning to automatic variables that de facto are read-only (e.g., $Host), but not about those that aren't read-only, but should be (e.g., $args); additionally, the rule must be activated manually, and this activation isn't persisted.
See the relevant feature request on GitHub.

Unfortunately, you typically cannot infer whether a variable name refers to an automatic variable from its name and there is also no programmatic way of discovering them - reading the help topic is the only option; see the next section.


List of automatic variables that shouldn't be writable, but de facto are:

Note:

  • The list was generated with the command below, as of Windows PowerShell v5.1 - in PowerShell Core, the list is shorter, because some obsolete variables were removed.

    • The command relies on string parsing of the relevant help topic; such a method is brittle, but it is currently the only way to discover automatic variables, given that there is no reflection-based method.

    • This GitHub issue suggests implementing programmatic discoverability and also discusses the possibility of introducing a separate, reserved namespace for automatic variables.

  • Over time, new automatic variables could be introduced; hopefully, their read-only status, if appropriate, will be enforced on introduction.

    • Because automatic variables share a namespace with user variables, introducing any new automatic variable bears the risk of breaking existing code.
  • A select few automatic variables are legitimately writeable:

    • $OFS - the separator to use when stringifiying arrays.
    • $null - the special variable that not only represents a null value when read, but is also designed to allow assigning any value to it in order to discard it (not write it to the (success) output stream).
    • $LASTEXITCODE, in the form $global:LASTEXITCODE, to set the process exit code to use (though that is better done with exit <n>).
  • The automatic variables not listed below (e.g., $PID) already are effectively read-only (as they should be). The ones that aren't (listed below), fall into the following categories:

    • Those that always quietly discard the assigned value (e.g., $Input, $MyInvocation, $PSCmdlet)

    • Those that accept a custom value, but override (shadow) it in contexts where a value is automatically assigned (e.g., $args, which is automatically set as a local variable whenever a new script block (function / script) is entered; your use as a local parameter variable was effectively ignored).

    • There are hybrid cases, notably $_ / $PSItem, where assigned values are quietly discarded outside any context where $_ is automatically assigned a value, but inside such contexts you can (but shouldn't) assign a new value (e.g, 'in' | ForEach-Object { $_ = $_ + '!'; $_ } outputs 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" refers to whether they exist in the global scope by default.

The following code was used to detect them - you can set $VerbosePreference = 'Continue' beforehand (and reset it after) to also see the already read-only variables:

$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 }
    }
  }
}

Note that the code has a hard-coded list of exceptions so as not to report automatic variables that should indeed be writable, such as $OFS, and $LastExitCode, or assignable as a quiet no-op, such as $null.



来源:https://stackoverflow.com/questions/58818197/passing-arguments-as-array-to-powershell-function

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!