Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing string[] from batch file (which contains double quotes ") to powershell script

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?

like image 495
Nikhil Agrawal Avatar asked Feb 23 '18 23:02

Nikhil Agrawal


People also ask

How do you pass double quotes in a PowerShell string?

This means that if you hard code a Distinguished Name in PowerShell, and the string is enclosed in double quotes, any embedded double quotes must be escaped first by a backtick "`", and then by a backslash "\".

How do you pass quotes in PowerShell?

To pass a double quote, escape with a backslash: \" -> " To pass a one or more backslashes followed by a double quote, escape each backslash with another backslash and escape the quote: \\\\\" -> \\" If not followed by a double quote, no escaping is necessary for backslashes: \\ -> \\

What is the difference between single and double quotes in PowerShell?

There is no such difference between the single quote (') and double quote(“) in PowerShell. It is similar to a programming language like Python. We generally use both quotes to print the statements.


1 Answers

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 `$.

like image 171
mklement0 Avatar answered Sep 19 '22 17:09

mklement0