regexwindowspowershellpowershell-7

Trouble with Regular Expression matching in PowerShell when lines contain "$" sign


I am trying to perform search and replace using RegExs in PowerShell like the following:

Julie found "NoMoney" in "Easter Island" => Julie("Easter Island") = "NoMoney"

John found $SomeMoney$ in "Greenland" => John("Greenland") = $SomeMoney$

Jimmy found $$LotsOfMoney in "Treasure Island" => Jimmy("Treasure Island") = $$LotsOfMoney

Judy found $5 in Tribeca.NY => Judy(Tribeca.NY) = $5

Because the "$" sign has a special meaning in PowerShell REs I am unable to do this successfully for all but the first test where there is no "$". I have tried to escape the $ with backslashes, backticks, and tried combining both as well but no luck.

Can someone help?


Solution

  • Because the "$" sign has a special meaning in PowerShell REs I am unable to do this successfully for all but the first test where there is no "$". I have tried to escape the $ with backslashes, backticks, and tried combining both as well but no luck.

    You don't need to match a literal $ to describe the listed strings at all:

    $pattern = '^(?<who>\p{Lu}[\w\s]+?) found (?<what>.+?) in (?<where>.+)$'
    
    $testStrings = @(
      'Julie found "NoMoney" in "Easter Island"'
      'John found $SomeMoney$ in "Greenland"'
      'Jimmy found $$LotsOfMoney in "Treasure Island"'
      'Judy found $5 in Tribeca.NY'
    )
    
    $testStrings |ForEach-Object {
      if ($_ -match $pattern) {
        "${_} => {0}({1}) = {2}" -f $Matches['who','where','what']
      }
    }
    

    Outputs:

    Julie found "NoMoney" in "Easter Island" => Julie("Easter Island") = "NoMoney"
    John found $SomeMoney$ in "Greenland" => John("Greenland") = $SomeMoney$
    Jimmy found $$LotsOfMoney in "Treasure Island" => Jimmy("Treasure Island") = $$LotsOfMoney
    Judy found $5 in Tribeca.NY => Judy(Tribeca.NY) = $5
    

    The pattern used above describes:

    ^                   # start of string
    (                   
      ?<who>            
      \p{Lu}            # 1 upper-case letter
      [\w\s]+?          # lazily followed by 1 or more word- or whitespace characters 
    )                   
     found              # literal substring " found "
    (                   
      ?<what>           
      .+?               # lazily followed by at least 1 of any character
    )                   
     in                 # literal substring " in "
    (                   
      ?<where>          
      .+                # followed by at least 1 of any character
    )                   
    $                   # end of string
    

    All the grouping constructs of the form (?<label>pattern) are named capture groups, allowing us to attach labels to matched/captured substrings.

    The $Matches variable used to store the capture group values is PowerShell-specific, see about_Regular_Expressions for more information


    Although this question appears to arise from an XY problem, for completeness here is the correct escape sequence for a literal $ in a .NET regex pattern:

    \$
    

    You can also produce this subpattern programmatically with [regex]::Escape:

    PS ~> [regex]::Escape('$')
    \$