Search code examples
powershellescaping

What is the difference between the escape backtick "`" and backslash "\" in PowerShell?


I know that both are used in PowerShell but for different contexts.

On the internet there is a paucity of information on this topic and the only site that talks about it (without making me understand the concept) is:
https://www.rlmueller.net/PowerShellEscape.htm

I am a beginner with PowerShell, I am recently approaching it.
A use case of the \ escape came up in the answer to this other topic of mine:
PowerShell removes multiple consecutive whitespaces when I pass arguments to a nested Start-Process command

Is there anyone who can explain to me in detail the difference between the escape backtick ` and backslash \ in PowerShell, with examples and use cases?

At least one source is welcome, but it is not mandatory.


Solution

  • vonPryz's helpful answer covers the PowerShell-internal angle well; let me attempt a systematic summary that includes the PowerShell CLI angle, as well as passing arguments with embedded " to external programs:

    Inside a PowerShell session, the only escape character is ` (the so-called backtick), in the following contexts:

    • Inside an expandable string ("...", double quoted), but not inside a verbatim string ('...', single-quoted); for the supported escape sequences, see the conceptual about_Special_Characters help topic:

      # " must be escaped; escape sequence `n expands to a newline.
      "3`" of`nrain" 
      
    • In unquoted command arguments:

      # > must be escaped to write verbatim 'a>b', 
      # since outside a quoted string an unescaped > is a redirection.
      Write-Output a`>b  
      
    • For line-continuations:

      # Spread the Get-Date call across two lines.
      # Important: ` must be the *very last char* on the line.
      Get-Date `
        -Year 2525
      
    • Note: Various subsystems, whether PowerShell-specific or not, may have their own escaping rules, such as \ in regular expressions and ` in wildcard expressions. Since arguments to those subsystems are delivered via PowerShell strings, it's best to use verbatim string literals, so as to avoid confusion between PowerShell's own string interpolation and what the target subsystem ends up seeing; e.g. 'A $50 fine.' -match '\$50' (\ is needed to treat regex metacharacter $ literally).


    When PowerShell is called from the outside, via its CLI, different rules apply, possibly in addition:

    In order to adhere to widely used convention for CLIs (command-line interfaces, programs that accept arguments via a command line) on Windows:

    • In calls to powershell.exe, the Windows PowerShell CLI, " characters must be escaped with a backslash - i.e. as \" - in order to be preserved during parsing of the original command line.

    • pwsh.exe, the CLI of the cross-platform, install-on-demand PowerShell (Core) 7+ edition, now commendably alternatively accepts ""[1] in lieu of \", which makes calls from cmd.exe more robust. To get the same robustness in Windows PowerShell - from cmd.exe only - use "^"" (sic).
      Note that - unlike \" - these escape sequences only work inside an (unescaped) "..." string (e.g., pwsh.exe -c " ""you & I"" " or powershell.exe -c " "^""you & I"^"" "

    By contrast, unescaped " have syntactic function on the command line and tell PowerShell where the boundaries between arguments are; these " instances are removed during command-line parsing.

    This ensures that outside callers that merely want to invoke a PowerShell script file (.ps1) with arguments, using the -File parameter, can use the conventional syntax and needn't special-case calls to PowerShell's CLI.

    However, if you pass a string containing PowerShell code to the CLI, using the -Command parameter, what PowerShell ends up interpreting obviously must be syntactically valid PowerShell code.

    Caveat: If you specify neither -Command nor -File:

    • powershell.exe defaults to -Command
    • pwsh.exe now defaults to -File

    For the differences between -File and -Command calls and when to use which, see this answer.

    If you use -Command, there are two, sequential parsing stages:

    • The command-line parsing stage, where syntactic (unescaped) " are removed, and escaped \" (or "") turn into literal ".

    • The result of this stage is then parsed as PowerShell code, as it would be from inside a PowerShell session.

    Therefore, you may situationally have to combine \ and `-escaping; e.g. (call from cmd.exe):

    C:>powershell.exe -Command " \"3`\" of snow as of $(Get-Date)\" "
    
    3" of snow as of 11/04/2021 14:13:41
    

    Note the use of `\" in order to make PowerShell see `", i.e. a properly escaped " inside a "..." string, after command-line parsing.

    Alternatively, depending on the specifics of the command(s) you pass to -Command, using embedded '...' quoting may be an option, which simplifies matters, because ' chars. don't require escaping:

    C:>powershell.exe -Command " '3\" of snow as of today.' "
    
    3" of snow as of today.
    

    Given that '...' strings in PowerShell are verbatim strings, use of '...' is only an option if you don't require string interpolation (such as the $(Get-Date) subexpression in the prior example).


    Escaping " when calling external programs from PowerShell:

    As a shell, it is PowerShell's job to pass the arguments that were passed based on its syntax rules to a target executable, so that the verbatim values that result from PowerShell's parsing are passed in a way that makes the target executable see them as such. In other words: PowerShell should perform any required escaping automatically, behind the scenes. (Unlike cmd.exe, PowerShell cannot just pass its own argument syntax through as-is, because external CLIs cannot be expected to understand '...' strings (single-quoting) or `-escaping).

    To use a simply example: Passing '3" of snow' should be passed as "3\" of snow" behind the scenes, based on the most widely used escaping convention.

    Sadly, up to PowerShell 7.2.x, this is not the case, and embedded " characters in arguments for external programs must additionally, manually be \-escaped in order to be passed through properly.

    # Broken behavior up to PS v7.2.x
    
    PS> cmd /c echo '{ "foo": "bar" }'
    "{ "foo": "bar" }"  # !! Embedded " aren't escaped.
    
    PS> choice.exe /d Y /t 0 /m '{ "foo": "bar" }'
    { foo: bar } [Y,N]?Y  # !! When parsed by an executable, 
                          # !! embedded " are effectively LOST.
    
    # Manual escaping required.
    PS> choice.exe /d Y /t 0 /m '{ \"foo\": \"bar\" }'
    { "foo": "bar" } [Y,N]?Y  # OK
    

    This bug has existed since v1, and has never been fixed so as to avoid breaking existing workarounds.


    [1] Inside PowerShell, in "..." strings only, you may also use "" to escape an embedded ", as an alternative to `"