Indrid
Indrid

Reputation: 1192

Powershell -replace giving strange behaviour

This is driving me nuts! Would appreciate any pointers as to what is going on, please!

$alpha = 'aaa.bbb.$connection.ccc.ddd'
$s = [regex]::match($alpha,"\$.+?(?=\.)").Value

"Alpha is: $alpha"
"Matched chunk is: $s"

$newChunk = "'" + $s + "'"
"New chunk is: $newChunk"

$beta = $alpha -replace $s,$newChunk
"Beta is: $beta"

This produces the following output:

Alpha is: aaa.bbb.$connection.ccc.ddd
Matched chunk is: $connection
New chunk is: '$connection'
Beta is: aaa.bbb.$connection.ccc.ddd

I am trying to identify/match character sequences in a path that begin with the literal '$' and continue until, but does not include a literal dot "." char. In the example code above the string would be "$connection".

Then I need to wrap that string in single quotes, so in the example above the $newChunk value would become '$connection' including the single quotes.

Next, I need to replace the matched value with the new single quoted value in the original string. No matter what I try (same with [regex]::match method too), the single quotes are stripped out. So, effectively, I am trying to turn:

aaa.bbb.$connection.ccc.ddd

into

aaa.bbb.'$connection'.ccc.ddd

Using Powershell 7.1 and would really appreciate someone telling me why this does not work. Thanks!

Upvotes: 2

Views: 187

Answers (2)

mklement0
mklement0

Reputation: 437638

Ryszard Czech's helpful answer is definitely the best solution: generically referring to what the regex-based -replace operator matched, in its replacement operand, with placeholder $& is the simplest and most robust solution.

See this answer for other placeholders you can use, such as what preceded ($`) or succeeded ($') the match, or what a capture group matched (e.g. $1).


As for what you tried:

  • Instead of using your regex \$.+?(?=\.) directly with the regex-based -replace operator, you used the verbatim result of an explicit [regex].Match() call as the search operand (first RHS operand) for -replace, which is then again interpreted as a regex.

    • However, in order to use a string verbatim as the -replace search operand, any regex metacharacters - i.e., characters with special syntactic meaning - must be \-escaped, such as the verbatim $ in your string, which you can do with [regex]::Escape($s)
  • Additionally, you used a replacement operand that had an embedded $ char. that you meant to be interpreted verbatim, but $ in the replacement operand is a metacharacter, referring to placeholders such as $&.

    • In order to use a string verbatim as the -replace replacement operand, you must programmatically escape embedded $ chars. as $$[1]:
      The simplest approach is to use the [string] type's literal-substring replacement method, .Replace():

      $replacementOperand = '$foo'    
      'value is: (value)' -replace '\(value\)', $replacementOperand.Replace('$', '$$') 
      

Thus, your original approach would have had to use the following command - but do note that Ryszard's solution is much simpler overall:

$beta = $alpha -replace [regex]::replace($s), $newChunk.Replace('$', '$$')

[1] Situationally you may get away without escaping, because a $-prefixed token that isn't recognized as a placeholder is left untouched (e.g., 'bar' -replace 'r', '$x' behaves the same as 'bar' -replace 'r', '$$x'), but the only robust solution is to escape verbatim $ chars. by doubling them.

Upvotes: 1

Ryszard Czech
Ryszard Czech

Reputation: 18611

Use

$beta = $alpha -replace '\$[^.]+', "'$&'"

See proof.

Explanation

--------------------------------------------------------------------------------
  \$                       '$'
--------------------------------------------------------------------------------
  [^.]+                    any character except: '.' (1 or more times
                           (matching the most amount possible))

The $& is the placeholder for the entire match.

Upvotes: 3

Related Questions