powershellargumentscommand-line-interfacequoting

Start-Process with PowerShell.exe exhibits different behavior with embedded single quotes and double quotes


First, in case anyone wonders why we're invoking PowerShell in this way, I ran into this behavior with a more complex command we were building, but the behavior can be exhibited using a more simple example as shown below. In practice, we are running a command under 32-bit PowerShell as admin with additional variables rendered in the string (hence why I don't simply use single-quotes for the outer portion), but that doesn't seem to factor into the behavior below.


When I invoke PowerShell through Start-Process, I get some odd behaviors if I use single quotes surrounding the -Command parameter to the PowerShell executable. For example:

Start-Process -FilePath Powershell.exe -ArgumentList "-Command 'ping google.com'"

just renders ping google.com as the output and exits. However, if I use nested double-quotes instead of the single-quotes as follows:

Start-Process -FilePath Powershell.exe -ArgumentList "-Command `"ping google.com`""

ping runs and produces the expected output:

Pinging google.com [173.194.78.113] with 32 bytes of data:

Reply from 173.194.78.113: bytes=32 time=34ms TTL=45

Reply from 173.194.78.113: bytes=32 time=33ms TTL=45

Reply from 173.194.78.113: bytes=32 time=35ms TTL=45

Reply from 173.194.78.113: bytes=32 time=32ms TTL=45

Ping statistics for 173.194.78.113:

Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),

Approximate round trip times in milli-seconds:

Minimum = 32ms, Maximum = 35ms, Average = 33ms

Why does the command string simply render as-is instead of execute if I use the single-quotes for the -Command parameter instead of double-quotes?


Solution

  • js2010's helpful answer is correct in that the use of Start-Process is incidental to your question and that the behavior is specific to PowerShell's CLI (powershell.exe for Windows PowerShell, and pwsh.exe for PowerShell (Core) 7+):

    On Windows[1], there are two layers of evaluation to consider:

    Re (a):

    As most console programs on Windows do, PowerShell recognizes only " chars. (double quotes) - not also ' (single quotes) - as having syntactic function.[2]

    Re (b)

    Whatever arguments are the result - i.e. an array of possibly "-stripped tokens - are concatenated with a single space between them, and the resulting (single) string is then interpreted as PowerShell code.


    To explain this in the context of your example, with Start-Process taken out of the picture:

    Note: The following applies to calling from cmd.exe or any context where no shell is involved (including Start-Process and the Windows Run (WinKey-R) dialog). By contrast, PowerShell re-quotes the command line behind the scenes to always use ", if needed.
    To put it differently: the following applies to command lines as seen by PowerShell.

    Single-quoted command:

    # Note: This *would* work for calling ping if run from 
    #       (a) PowerShell itself or (b) from a POSIX-like shell such as Bash.
    #       However, via cmd.exe or any context where *no* shell is involved,
    #       notably Start-Process and the Windows Run dialog, it does not.
    powershell -Command 'ping google.com'
    

    Double-quoted command:

    powershell -Command "ping google.com"
    

    [1] On Unix-like platforms, the first layer doesn't apply, because programs being invoked only ever see an array of verbatim arguments, not a command line that they themselves must parse into arguments. Not that if you call the PowerShell CLI from a POSIX-like shell such as bash on Unix-like platforms, it is that shell that recognizes single-quotes as string delimiters, and strips them before PowerShell sees them.

    [2] Surprisingly, on Windows it is ultimately up to each individual program to interpret the command line, and some do choose to also recognize ' as string delimiters (e.g., Ruby). However, many programs on Windows - including PowerShell itself - are based on the C runtime, which only recognizes ".

    [3] As an aside, note that this implies that whitespace normalization is taking place: that is,
    powershell -Command 'ping google.com' would equally result in 'ping google.com'.