2

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!

Indrid
  • 962
  • 4
  • 25
  • 39
  • 1
    Thank you for the rich detail. Very helpful indeed! Yes, I thought that -replace would simply substitute string literals. It never occurred to me that the leading $ might be interpreted. Many thanks. – Indrid Oct 04 '20 at 22:45

2 Answers2

3

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.

Ryszard Czech
  • 18,032
  • 4
  • 24
  • 37
  • Beautiful answer and gets me going again - thank you! I would love to know why the original code didn't work if anyone knows? Thanks Ryszard – Indrid Oct 04 '20 at 18:56
  • @Indrid You used `$s`, holding `$connection`, that when used as a regex, matches the end of string followed with `connection`. This cannot happen. You would need to escape `$`, maybe using `[regex]::escape`. No need to do that though, `$&` is designed to be used in exactly cases like this. – Ryszard Czech Oct 04 '20 at 18:59
  • Outstanding. Thank you so much! – Indrid Oct 04 '20 at 19:00
1

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.

mklement0
  • 382,024
  • 64
  • 607
  • 775