1

I'm trying to update a group of XML based .config files. There is a string in the file that contains a plus sign that I cannot replace with my script:

< SharedPassKey=123456789abcdefghi/JKLM+nopqrst= />

If I include the plus sign, the script does nothing. Since all the configs vary I need to replace the text - cannot just go with new files.

The desired result is that the matching value in the file be replaced with the specified value, but the + symbol is not allowing this. Here is my PS script:

$DIRs = Get-ChildItem -Path "C:\TEST" -Directory 
Get-ChildItem $DIRs -File -Recurse -Filter *.config | 
    ForEach { (Get-Content $_.FullName) | 
    ForEach { $_ -replace '< SharedPassKey=123456789abcdefghi/JKLM+nopqrst= />','< SharedPassKey=123456789abcdefghi/JKLM.nopqrst= />' } | 
    Set-Content $_.FullName }

As a test I replaced all the text up to the plus sign and it worked fine, but the plus sign and following were present at the end of the new text as I had left the "+nopqrst" out of the script.

Running on PSv3, FYI.

Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
Charlie Echo
  • 67
  • 3
  • 11

2 Answers2

8

The -replace operator uses a regular expression for the first postfix argument to define the search pattern. In a regular expression, certain characters have a special meaning. + in particular is a so-called "quantifier" with the meaning "one or more times the preceding (sub)expression". In order to replace literal special characters, you need to escape them.

Fortunately, there's a built-in method for escaping strings:

$_ -replace [RegEx]::Escape('< SharedPassKey=123456789abcdefghi/JKLM+nopqrst= />'),'< SharedPassKey=123456789abcdefghi/JKLM.nopqrst= />'
Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
Bacon Bits
  • 30,782
  • 5
  • 59
  • 66
  • 1
    Beware, though, that `[regex]::Escape()` will escape all special characters in the given string. If you just want to replace one literal (sub)string with another you may want to consider using the `String.Replace()` method instead: `$_.Replace('foo', 'bar')`. – Ansgar Wiechers Aug 17 '17 at 18:32
  • Both are very helpful! @Ansgar, thanks for that tip, I've not used that method but will not shy away in the future. – Charlie Echo Aug 17 '17 at 19:33
  • @Bacon Bits solved this one for me! His adjustment to my script proved to be successful. Thank you! – Charlie Echo Aug 17 '17 at 19:42
0

If you have valid XML data and you want to modify the value of a node attribute in an XML file you may want to consider using a proper XML parser, like this:

$xmlfile = 'C:\path\to\your.config'

[xml]$xml = Get-Content $xmlfile
$attr = $xml.SelectSingleNode('//somenode/@SharedPassKey')
$attr.'#text' = $attr.'#text'.Replace('+', '.')

$xml.Save($xmlfile)

Make the XPath expression for selecting the attribute as specific as it needs to be. That will allow you to replace a + with a . in just that attribute.

Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
  • Hi Ansgar, I've been doing it this with -replace for a long time, but finally a special character stopped me in my tracks. I am not familiar with the method you provided and it doesn't seem the example you left will cover a multiple files in the in the sub-directories. I also don't know how to specify the string to be edited with this method. Obviously, I'm not expecting a lesson and I'm doing my own research/studying now, however, your input and previous comments are greatly appreciated. If I can learn this method it may prove to be more efficient. – Charlie Echo Aug 17 '17 at 19:54
  • The code is just an example of how to replace an attribute value using an XML parser. It's not meant to be a turnkey solution. Also, with this approach you don't specify a single string. You have an XPath expression for selecting the attribute(s) whose value you want to change, and a replacement operation for the value. – Ansgar Wiechers Aug 17 '17 at 20:02