6

I have a tool that accepts parameters like this (e.g. in test.ps1):

foo.exe -name="john"

So, each parameter is specified with a single hyphen -, the name, an equals =, and then the value of the parameter.

When I invoke this exact expression from PowerShell, it executes without any problems. However, when one of the values contains a period . like this:

foo.exe -name="john.doe"

Running it causes a syntax error:

$ ./test.ps1 The string starting: At test.ps1:1 char:24 + foo.exe -name="john.doe <<<< " is missing the terminator: ". At test.ps1:1 char:25
+ foo.exe -name="john.doe" <<<<
+ CategoryInfo : ParserError: (:String) [], ParseException
+ FullyQualifiedErrorId : TerminatorExpectedAtEndOfString

Some ways that I can prevent PowerShell from interpreting this are:

  • foo.exe "-name=`"john.doe`""
  • foo.exe '-name="john.doe"'
  • PowerShell V3+: foo.exe --% -name="john.doe"
  • $nameArg = "john.doe"; foo.exe -name="$nameArg"

However, some of these options prevent interpolation of variables. Are there other ways to stop PowerShell from causing syntax issues? In this specific instance (adding the period), why is PowerShell having issues interpreting this?

G42
  • 9,791
  • 2
  • 19
  • 34
TheCloudlessSky
  • 18,608
  • 15
  • 75
  • 116

2 Answers2

4

What you're seeing is a bug in PSv2; in short: an argument that starts with unquoted - breaks parsing if it also contains a . within "...".

A milder variation of the bug still exists in PowerShell v5.1 / PowerShell Core v6.0.1; it has been reported on GitHub. Now, a . inside "..." works fine, but an unquoted . actually breaks the argument in two. The workaround below is also effective for said variation - see this answer of mine.

The workaround is to quote the entire argument:

# Use '...' instead if you don't need interpolation
foo.exe "-name=john.doe"

Note that there is typically no need to quote the value part individually - that is, to the target program,
-name="john.doe 1" is usually the same as "-name=john.doe 1"

mklement0
  • 382,024
  • 64
  • 607
  • 775
0

I've run into this before, and I don't know if it is the best way to get around it but one way that I have done it before was to build the command string to execute and then execute it using Invoke-Expression.

$MyCommand = '& foo.exe --% -name="{0}"' -f 'john.doe'
Invoke-Expression $MyCommand

Or, more specific to my issue, I would have several arguments that would change that I had in a hashtable, so I would add those to the command. In context to your command I might have:

$MyArgs = @{
    name = 'john.doe'
    request = 'delete file'
    argument = '\jdoe\temp\nsfwstash.zip'
}
$MyCommand = '& foo.exe --%'
$MyArgs.Keys | ForEach{
    $MyCommand = $MyCommand + (' {0}="{1}"' -f $_, $MyArgs[$_])
}
Invoke-Expression $MyCommand

That would end up invoking a command that read:

& foo.exe --% -name="john.doe" -request="delete file" -argument="\jdoe\temp\nsfwstash.zip"
TheMadTechnician
  • 34,906
  • 3
  • 42
  • 56