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:
- rigorously police all variables being fed to external commands, or
- use the CMD escape (--%) and forego using any PowerShell constructs on the rest of the line
- 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]);
}
}
}