6

This question is similar to Passing empty arguments to executables using powershell but I want to expand upon both the question and the answer and understand the "why" better, because it seems like a PowerShell pitfall.

If you load up PSCX and run echoargs (an external command, i.e. an exe file) you can see that the empty string argument is skipped:

PS> echoargs word "two words" "" 123
Arg 0 is <word>
Arg 1 is <two words>
Arg 2 is <123>

But if you use the "CMD escape" (--%) you can make it appear "properly":

PS> echoargs --% word "two words" "" 123
Arg 0 is <word>
Arg 1 is <two words>
Arg 2 is <>
Arg 3 is <123>

Similarly if one writes a PowerShell function the empty string is properly handled:

PS> Show-Args word "two words" "" 123
Arg 0 is <word>
Arg 1 is <two words>
Arg 2 is <>
Arg 3 is <123>

This difference seems like a big deal to me for the following reason. The examples as shown above use a literal empty string on the command-line so at least you have a hint of the problem. But the results are exactly the same if one uses a variable containing an empty string. Which means one has to either:

  1. rigorously police all variables being fed to external commands, or
  2. use the CMD escape (--%) and forego using any PowerShell constructs on the rest of the line
  3. quote every argument to an external command with backquote/double quote like `"this`" or `"$this`" or `"`"

...or bad things will happen!

(@KeithHill pointed out the third workaround above so I added it there for completeness. It works for literals or variables so, though ugly, is perhaps the best choice of the three workarounds.)

So PowerShell handles arguments to a function differently than arguments to an external command--dramatically so. Is this an inconsistency in PowerShell's behavior? If not, why not?


Addendum

For reference, here's the PowerShell function body used above:

function Show-Args()
{
    for ($i = 0; $i -lt $args.length; $i++)
    {
        Write-Host ("Arg {0} is <{1}>" -f $i, $args[$i])
    }   
}

And here is an echoargs-equivalent in C#:

class Program
{
    static void Main(string[] args)
    {
        for (int i = 0; i < args.Length; i++)
        {
            System.Console.WriteLine("Arg {0} is <{1}>", i, args[i]);
        }
    }
}
Community
  • 1
  • 1
Michael Sorens
  • 35,361
  • 26
  • 116
  • 172
  • Relevant github issue: [Arguments for external executables aren't correctly escaped #1995](https://github.com/PowerShell/PowerShell/issues/1995). – Dabombber Aug 14 '20 at 12:16

1 Answers1

2

The behavior might be considered "by design" (it's hard to know for sure based on the implementation and tests), but I don't think the matter was given much thought and you bring up excellent points.

Personally I think you are right and PowerShell is being inconsistent. It's worth considering a change to be consistent, though that has some risk of breaking existing scripts.

Jason Shirk
  • 7,734
  • 2
  • 24
  • 29
  • 1
    Yeah, the handling of empty string arguments to applications does seem, um, wrong. ;-) To be fair, this issue exists at least as far back as V2. – Keith Hill May 15 '14 at 03:52