Peace For All

September 25, 2011

PowerShell Call Operator (&): Using an array of parameters to solve all your quoting problems

Filed under: Life in general, PowerShell, Programming — Tags: , — Devlin Bentley @ 7:30 am

I would like to thank James Brundage (blog!) for telling me about this. Suffice to say, the man is seriously into automation.

Alright, if you just want to learn about using arrays of parameters with the call operator (&) and skip all the explanation of what doesn’t work, scroll down to the bottom. I am a big believer in understanding solutions though, so this post will detail everything that doesn’t work and slowly build up towards what does work.

The last blog post I did on this topic was about using Invoke-Expression to solve problems with passing parameters to external programs. I resorted to using Invoke-Expression since (as an undocumented side effect?) Invoke-Expression will strip off quotes from parameters to commands it executes. But in some circles using Invoke-Expression to execute programs is considered heresy. It is thanks to James Brundage that I was able to figure out how to better use & and also come to a greater conscious realization of how PowerShell handles strings.

To summarize the problem, try to get the following to run in PowerShell

$pingopts = "www.example.com -n 5"
ping $pingopts

If you run this command ping will spit out an error, the root cause of the problem is that PowerShell passes $pingopts to ping with the quotes still on it, so the above line is the same as typing

ping “www.example.com -n 5”

Which is obviously quite wrong.

The next obvious solution is to use the call operator, “&”. The call operator is how you tell PowerShell to basically act as if you had just typed whatever follows into the command line. It is like a little slice of ‘>’ in your script.

Now the call operator takes the first parameter passed to it and uses Get-Command to try to find out what needs to be done. Without going into details about Get-Command, this means the first parameter to the call operator must be only the command that is to be run, not including parameters. The people over at Powershell.com explain it really well.

With all this in mind, let us try the following

$pingopts = "www.example.com -n 5"
&ping $pingopts

Run that and you will get the exact same error. Fun!

Why is this happening?

The problem is that & does not dequote strings that have spaces in them.

So this code works:

$pingopts = "www.example.com"
&ping $pingopts

Where as

$pingopts = "  www.example.com"
&ping $pingopts

will not.

But if we think about this for a minute, we already know about this behavior. Heck we expect it and rely on it. It is so ingrained into how we use PowerShell that we don’t even think about it, except for when we run head first into it. So now let us explicitly discuss PowerShell’s handling of strings.

String Quoting Logic

The string auto quoting and dequoting logic is designed around passing paths around. The rule, as demonstrated above, is quite simple. A string with a space in it gets quoted when passed to something outside of PoSH, while a string without spaces in it has its quotes stripped away. This logic basically assumes if you have a space, you are dealing with a path and you need quotes. If you don’t have a space, you are either dealing with a path that doesn’t need quotes, or are passing something around that isn’t a path and you do not want quotes. For those scenarios PowerShell gives exactly the results people want, which just so happen to be the results people need 95% of the time.

Problems arise  when you have strings with spaces in them that you do not want quoted after leaving the confines of PowerShell. Bypassing the string quoting/dequoting logic is not easy and you can end up resorting to Invoke-Expression hacks like I detailed earlier or you can try to find a way to work within the system. The latter is obviously preferable.

The Solution

You may have already guessed the solution from the title of this blog post: Pass an array of parameters to the call operator. Given the sparse documentation available online for & (it would be nice if it said string[] somewhere), one has to have a fairly good understanding of Powershell to figure this out on their own, or just randomly try passing an array to &.

The key here is working the system: by passing parameters in an array you can avoid having spaces in your quoted strings. Where you would normally put a space, you break off and create a separate array element. This is still a bit of a work around, it would be optimal to find a way to tell & to dequote strings, but this solution does work.

Code:

$pingopts = @("www.example.com", "-n", 5)
&ping $pingopts

Again, notice instead of “-n 5”, I split it into two array elements.

Just for reference, here is how you would build that command up line by line using an array:

$pingopts = @()
$pingopts += "www.example.com"
$pingopts += "-n"
$pingopts += 5
&ping $pingopts

This actually is not much different from constructing 3 separate variables and passing them in after ping:

$param1 = "www.example.com"
$param2 = "-n"
$param3 = 5
&ping $param1 $param2 $param3

Which is the blatantly obvious solution but also the ugly one so I never even considered it. Of course using arrays is more flexible since you can declare at top and slowly build up your command line throughout your script.

Hopefully this saves everyone some time and the journey has helped you understand a bit more about Powershell.

Advertisements

9 Comments »

  1. Try also:

    $pingopts = “www.example.com -n 5”
    ping $pingopts.split()

    Comment by tdiet — September 27, 2011 @ 11:11 pm

    • Great idea! .split() at the end to break everything up into an array would also work. It all depends if you want to construct up from nothing piece by piece, or do a big bang and then shatter everything apart into base elements.

      Comment by Devlin Bentley — September 28, 2011 @ 12:26 am

  2. This works for me even without the call operator:

    $pingopts = “www.example.com”, “-n”, 5
    ping $pingopts

    Btw, there is a typo in your example (&pingopts):

    $pingopts = @(“www.example.com”, “-n”, 5)
    &ping &pingopts

    Comment by Aleksandar — September 28, 2011 @ 4:27 am

    • Your example builds up an array, just with looser syntax.

      Likewise & is not always needed by PowerShell, but it is good practice as it specifies that you are calling out to something external.

      Comment by Devlin Bentley — October 5, 2011 @ 1:19 pm

  3. This is a working example using sysinternals psinfo.exe command-line utility, where $_INST is the current script path with a backslash and $_PC is the name of a computer on the network.
    ————————————————————————————————————————————————————
    $_CMD_LINE = $_INST + ‘psinfo.exe’
    $_CMD_ARGS = @()
    $_CMD_ARGS += “-s”
    $_CMD_ARGS += “\\$_PC”

    $_SW_Result = &psinfo.exe $_CMD_ARGS
    ————————————————————————————————————————————————————

    $_SW_Result = &psinfo.exe @($_INST + ‘psinfo.exe’,”-s”,”\\$_PC”)

    ————————————————————————————————————————————————————
    Both return the installed software on the remote machine.

    Thank you Gentlemen for this example, for Systems Engineers who script for automation and aren’t stone-cold coders this makes life infinitly easier.
    The object of the game is to get the result you want. NOT spend hours of coding in powershell to duplicate what a command-line utility can already do.
    This clear, concise, intelligent answer has save me MANY hours. KUDOS!

    Comment by John Delise — November 22, 2012 @ 11:25 am

  4. Reblogged this on .Net Diaries.

    Comment by Michael Denny — February 23, 2015 @ 6:23 am

  5. http://web-ide.ir/ranking/index.php?a=stats&u=carolinevansickl

    PowerShell Call Operator (&): Using an array of parameters to solve all your quoting problems | Peace For All

    Trackback by http://web-ide.ir/ranking/index.php?a=stats&u=carolinevansickl — March 31, 2017 @ 6:46 pm

  6. There is definately a great deal to find out about
    this issue. I like all the points you have made.

    Comment by travel europe expo perth — April 21, 2017 @ 9:48 pm


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: