Home > Software design >  Powershell, use (space-separated) string as arguments to a program
Powershell, use (space-separated) string as arguments to a program

Time:09-16

I've read this and it doesn't solve my problem.

I have a space-separated string, let's say $MyString = "arg1 arg2". Suppose I have a command line program called MyProgram, which accepts an arbitrary number of positional arguments, so it can be run like MyProgram arg1 arg2. However doing MyProgram $MyString doesn't work, and neither does MyProgram ($MyString -split ' ') nor MyProgram $($MyString -split ' '). I get the same error which basically says that it doesn't recognise the argument "arg1 arg2", which I guess is because it still thinks it's one argument containing a space rather than two arguments. In practice, $MyString may be quite huge and is read from a file. How do I make this work?

CodePudding user response:

Oh I just found out how LOL. I should have thought of this sooner; basically, just use splatting The following worked for me:

$MyArray = $($MyString -split " ")
MyProgram @MyArray

Explanation: The first line converts the string into an array of strings split by space (" "); The $(...) notation around a command captures the output of the command, which I then assign to $MyArray. Then, instead of using $MyArray with a dollar sign $, I use it with @ to splat the array of strings into arguments for MyProgram.

CodePudding user response:

tl;dr

For calling PowerShell commands you indeed need splatting in order to pass the elements of an array as individual, positional arguments; this requires defining the array in an auxiliary variable that can then be passed with sigil @ in lieu of $ to request splatting:

$myArray = -split $myString   # See below for limitations, bottom section for fix
MyPowerShellCommand @myArray  # Array elements are passed as indiv. arguments.

While this technique also works with external programs, it isn't strictly necessary there, and you can pass an array directly to achieve the same effect:

MyExternalProgram (-split $myString) # Array elements are passed as indiv. args.

Note that (...) rather than $(...) is used to pass the expression as an argument. (...) is usually sufficient and generally preferable, because $(...) can have side effects - see this answer for details.


Just to bring the post you link to in your question and your answer here together:

First, to be clear: neither answer, due to splitting by spaces only will deal properly with arguments inside the argument-list string that have embedded spaces (and therefore, of necessity use embedded quoting), e.g., $myString = "arg1 `"arg2 with spaces`" arg3" would not work as expected - see the bottom section for a solution.

Leaving that aside, the difference is:

  • When calling an external program, as in the linked post, passing an array causes each element to become its own argument.

    • That is, myExternalProgram (-split $MyString) would work.
    • Note that I'm using the unary form of the -split operator for more flexible tokenization, which splits by any non-empty run of whitespace while ignoring leading and trailing whitespace (same as awk's default behavior).
  • When calling a PowerShell command, as in your case, an array is by default passed as-is, as a whole, as a single argument.

    • To achieve the same effect as with external programs, i.e. to pass the array's elements as individual, positional arguments, you indeed have to use splatting, i.e. you have to:

      • save the array in a variable first: $myArray = -split $myString,
      • which you can then pass as as a splatted argument by using @ instead of $ as the sigil: MyPowerShellCommand @myArray
    • Do note that when calling PowerShell commands it is more common - and more robust - to use hashtable- rather than an array-based splatting, as it allows you to explicitly bind to parameters by name rather than by position - and PowerShell commands often have parameters that can only be bound by name.

      • E.g., if MyPowerShellCommand accepts parameters -Foo and -Bar, you could use:
        $myArgs = @{ Foo='foo value'; Bar='bar value '}; MyPowerShellCommand @myArgs

If you do want to handle argument-list strings that have arguments with embedded spaces:

$myString = "arg1 `"arg2 with spaces`" arg3"

$myArray = (Invoke-Expression ('Write-Output -- '   $myString -replace '\$', "`0")) -replace "`0", '$$'

Note: Invoke-Expression (iex) should generally be avoided, but the extra precautions taken in this particular command make its use safe.

$myArray is then a 3-element array with verbatim elements arg1, arg2 with spaces and arg3, which can again be used as shown above.

See this answer for an explanation of the technique.

CodePudding user response:

These work for me ($args is reserved). -split on the left side splits on whitespace. Or you can get-content from a file where each argument is on a seperate line. You might run into a limit with how long a commandline can be. Piping that list in or loading it from a file might be a better approach.

echo hi > file.txt

$args2 = 'hi','file.txt'
findstr $args2
# hi

$args2 = 'hi','file.txt'
& findstr $args2
# hi

$args2 = 'hi file.txt'
findstr (-split $args2)
# hi

findstr ($args2 -split ' ')
# hi
  • Related