Powershell -replace when replacement string contains $+

前端 未结 4 1851
被撕碎了的回忆
被撕碎了的回忆 2020-12-07 00:08

I am doing a string replacement in PowerShell. I have no control over the strings that are being replaced, but I can reproduce the issue I\'m having this way:



        
4条回答
  •  时光说笑
    2020-12-07 00:26

    tl;dr:

    Double the $ in your replacement operand to use it verbatim:

    PS> 'word' -replace 'word', '@#$$+' # note the doubled '$'
    @#$+
    

    PowerShell's -replace operator:

    • uses a regex (regular expression) as the search (1st) operand.

      • If you want to use a search string verbatim, you must escape it:
        • programmatically: with [regex]::Escape()
        • or, in string literals, you can alternatively \-escape individual characters that would otherwise be interpreted as regex metacharacters.
    • uses a non-literal string that can refer to what the regex matched as the replacement (2nd) operand, via $-prefixed tokens such as $& or $+ (see link above or Substitutions in Regular Expressions).

      • To use a replacement string verbatim, double any $ chars. in it, which is programmatically most easily done with .Replace('$', '$$') (see below).

    If both your search string and your replacement string are to be used verbatim, consider using the [string] type's .Replace() method instead, as shown in Brandon Olin's helpful answer.

    • Caveat: .Replace() is case-sensitive by default, whereas -replace is case-insensitive (as PowerShell generally is); use a different .Replace() overload for case-insensitivity, or, conversely, use the -creplace variant in PowerShell to get case-sensitivity.

      • Case-insensitive .Replace() example:
        'FOO'.Replace('o', '@', 'CurrentCultureIgnoreCase')
    • .Replace() only accepts a single string as input, whereas -replace accepts an array of strings as the LHS; e.g.:

      • 'hi', 'ho' -replace 'h', 'f' # -> 'fi', 'fo'
    • .Replace() is faster than -replace, though that will only matter in loops with high iteration counts.


    If you were to stick with the -replace operator in your case:

    As stated, doubling the $ in your replacement operand ensures that they're treated verbatim in the replacement:

    PS> 'word' -replace 'word', '@#$$+' # note the doubled '$$'
    @#$+
    

    To do this simple escaping programmatically, you can leverage the .Replace() method:

    'word' -replace 'word', '@#$+'.Replace('$', '$$')
    

    You could also do it with a nested -replace operation, but that gets unwieldy (\$ escapes a $ in the regex; $$ represent a single $ in the replacement string):

    # Same as above.
    'word' -replace 'word', ('@#$+' -replace '\$', '$$$$')
    

    To put it differently: The equivalent of:

    'word'.Replace('word', '@#$+')
    

    is (note the use of the case-sensitive variant of the -replace operator, -creplace):

    'word' -creplace [regex]::Escape('word'), '@#$+'.Replace('$', '$$')
    

    However, as stated, if both the search string and the replacement operand are to be used verbatim, using .Replace() is preferable, both for concision and performance.

提交回复
热议问题