-->

Passing string[] from batch file (which contai

2020-03-26 04:07发布

问题:

I have a powershell script which has a string[] parameter and try to pass value to this from batch file

PowerShell Script

[string[]]$ScriptArgs

Batch File

powershell -File Foobar.ps1 -ScriptArgs -versn="""1.0.0.0""",pattern="""FooBar.*"""

(3 Quotes - For escaping/showing 1 quote)

But no matter what I do Write-Host $ScriptArgs.Count prints 1

I want the variable to receive 2 elements

[0] will be -versn="1.0.0.0"
[1] will be -pattern="FooBar.*"


In batch file, I even tried setting variables individually

set @sArgs = -versn="""1.0.0.0"""
set @sArgs = %sArgs%;-pattern="""FooBar.*"""
powershell -File Foobar.ps1 -ScriptArgs %sArgs%

but the console somehow runs as if the variable is not created and run it as

powershell -File Foobar.ps1 -ScriptArgs

and throws error

Missing an argument for parameter 'ScriptArgs'. Specify a parameter of type System.String[] and try again

What should I change to achieve this?

回答1:

When calling a PowerShell script from from a batch file (from cmd.exe) with -File, there is no way to directly pass an array of values to a PowerShell array parameter:

  • If you specify the array values without spaces, they are considered a SINGLE element; e.g.:

    • one,two,three becomes a SINGLE array element
  • If you do use spaces, the trailing commas become part of the arguments, and only the FIRST value is bound to the array parameter:

    • one, two, three - one, - including the trailing , - becomes the only array element, two, and three, are considered separate arguments.

The workarounds are to:

  • Either: use -Command to invoke the script - see bottom section.

  • Or, with -File, declare the parameter differently and pass arguments differently:

    • decorate the array parameter with attribute [Parameter(ValueFromRemainingArguments)]
    • and to pass the array elements as individual, space-separated arguments, without their parameter name.

In your case:

  • Declare the parameter:

    [Parameter(ValueFromRemainingArguments)]
    [string[]] $ScriptArgs
    
  • Invoke the script with separate, space-separate arguments not preceded by the parameter name to bind them as an array to $ScriptArgs:

    powershell -File Foobar.ps1 "-versn=\"1.0.0.0\"" "pattern=\"FooBar.*\""
    
    • Note the quoting:

      • Each argument as a whole is enclosed in "..." - this isn't strictly necessary, but guards against --prefixed values such as -version being mistaken for parameter names.

      • " chars. to be retained as literals are \-escaped (which contrasts with the `-escaping that must be used from within PowerShell).

The inherent limitations of this approach:

  • The approach doesn't support multiple array parameters, because only one can be decorated with ValueFromRemainingArguments.

  • You cannot pass any other arguments positionally (i.e., you must precede arguments to pass to other parameters with their parameter name).


Alternative: Using -Command instead of -File:

powershell -command "./Foobar.ps1 '-versn=\"1.0.0.0\"', '-pattern=\"FooBar.*\"'"

Note:

  • What you pass to -Command is treated as a piece of PowerShell code - whether you pass a single argument or multiple ones - in the latter case, they are simply concatenated with spaces and the result is then interpreted as PowerShell code.

  • The implications are:

    • To execute a script in the current directory, you must refer to it with a path (.\) - in order to execute it.

      • Additionally, if the script path contains whitespace or other shell metacharacters, you must quote it and invoke it via &, the call operator; e.g., & '.\Foo Bar.ps1'
    • You can pass array arguments as usual, except that " chars. must again be escaped as \", not `".

    • Also note that again the arguments are quoted as a whole (too), which in this is case required to prevent PowerShell from interpreting -versn and -pattern as parameter names.

      • Given that the overall command must be passed inside "...", performing the embedded quoting with ", which still possible, is quite awkward, because you then have to combine \- and `-escaping; e.g., \"-pattern=`\"FooBar.*`\"\"
    • Generally, be aware that all usual PowerShell parsing rules apply, so that, for instance, to use a literal $, you must escape it as `$.