powershellescapingquotesdouble-quotessingle-quotes

PowerShell: escaping rules for CLI calls


As I have learned, when invoking PowerShell from cmd.exe, with the -Command option, escaping rules are essentially those used in Linux. So you use a backslash (\), rather than the usual backtick (`).

This is to say that you don't write:

C:\> powershell -c "echo `"That's a single quote: ' `""

but

C:\> powershell -c "echo \"That's a single quote: ' \""

to get:

That's a single quote: '

And this is the exact equivalent of what you would type in a Linux terminal:

~ >>> bash -c "echo \"That's a single quote: ' \""                                                                      

If I am not wrong, this feature is named PSNativeCommandArgumentPassing. But comparisons work up to a certain point. In fact, under Linux you write:

~ >>> bash -c "echo \"That's a double quote: \\\" \""                                                              

to get:

That's a double quote: " 

while the equivalent PowerShell line:

C:\> powershell -c "echo \"That's a double quote: \\\"  \""

gives

The string is missing the terminator: ".

By trial and error, I realised that:

C:\> powershell -c "echo \"That's a double-quote: `""  \""

works as expected.

Can you explain to me what is the logic behind: `""?

Also, what are the equivalent commands when calling PowerShell from powershell.exe, rather than cmd.exe?


Solution

  • tl;dr


    this feature is named PSNativeCommandArgumentPassing

    No; this feature - which became official in PowerShell 7.3 (see this answer) - does not come into play, because:


    Why the following works from cmd.exe:

    powershell -c "echo \"That's a single quote: ' \""
    

    Therefore, what PowerShell ends up executing is verbatim:

    echo "That's a single quote: ' "
    

    From the above follows why this does not work:

    :: SYNTAX ERROR
    powershell -c "echo \"That's a double quote: \\\"  \""
    

    PowerShell ends up trying to execute verbatim

    echo "That's a double quote: \" "
    

    which is a syntax error, because inside a PowerShell session \ doesn't escape " -only `" or - inside "...", alternatively - "" do.


    From the above follows why this (mostly) works:

    :: Works, but not robustly
    powershell -nop -c "echo \"That's a double-quote: `""  \""
    

    To avoid the brittle "" escaping, it is better to formulate this command by combining the required `-escaping with the command-line \-escaping - i.e. `\" - but see the bottom section for a fully robust solution:

    powershell -nop -c "echo \"That's a double-quote: `\"  \""
    

    Avoiding parsing problems on the cmd.exe side, a safe alternative to \":

    While \" to escape " works consistently in both PowerShell editions on the PowerShell (command-line parsing) side, situationally it can break cmd.exe's own parsing. By contrast, using \" is safe when calling from no-shell environments such as Task Scheduler.

    While there are workarounds, they are, unfortunately PowerShell edition-specific:

    Important:

    The following calls, designed to print verbatim  Between 2 & 3" , demonstrate this:

    :: BREAKS, because cmd.exe sees `&` as *outside a double-quoted string*
    powershell -c " \" Between 2 & 3`\" \" "
    
    :: PowerShell (Core) workaround
    pwsh -c " "" Between 2 & 3`"" "" "
    
    :: Windows PowerShell workaround
    powershell -c " "^"" Between 2 & 3`"^"" "^"" "
    

    [1] An example of where "" inside "..." doesn't work is powershell -c "echo \" Nat `""King`"" Cole \"": instead of Nat "King" Cole, it prints Nat "King Cole, i.e. the second escaped " is missing (it would work fine in pwsh.exe, however, as discussed in the bottom section). It's ultimately not worth speculating how, precisely, embedded "" sequences are parsed by powershell.exe -c, given that it demonstrably isn't reliable and that reliable alternatives do exist (\" or, from cmd.exe, also "^"").