Keith Langmead
Keith Langmead

Reputation: 1157

Updating a string from XML using PowerShell

I'm retrieving a string of text from some XML data, and then trying to replace that string with something else. The code I'm using is:

$newstring = "mystring"
$bar = Get-Gpo -Name "My GPO"
$gpoid = $bar.Id.ToString()
$drivesxml = New-Object XML
$drivesxml.Load("\\mydomain.co.uk\sysvol\mydomain.co.uk\Policies\{gpoid}\User\Preferences\Drives\drives.xml")
$oldpath = $drivesxml.Drives.Drive.Properties.Path[0]
$oldlabel = $drivesxml.Drives.Drive.Properties.Label[0]
#1
[string]$newtemppath = $oldpath -replace "Oldstring$", "$newstring"
[string]$newtemplabel = $oldlabel -replace "^Oldstring", "$newstring"
#2
$drivesxml.Drives.Drive.Properties.Path[0] = $newtemppath
$drivesxml.Drives.Drive.Properties.Label[0] = $newtemplabel
#3
$drivesxml.Save("\\mydomain.co.uk\sysvol\mydomain.co.uk\Policies\{gpoid}\User\Preferences\Drives\drives.xml")

It retrieves the XML from sysvol fine, and at point #1 if I query $oldpath and $oldlabel they contain the expected text from the XML.

At #2 if I query $newtemppath and $newtemplabel the strings are returned as expected with the text amended so instances of "Oldstring" have been replaced by "mystring".

At #3 if I query $drivesxml.Drives.Drive.Properties.Path[0] and $drivesxml.Drives.Drive.Properties.Label[0] I'd expect them to return the same content as the $newtemppath and $newtemplabel variables, but instead they continue to return their original values.

After saving the XML if I query it again the content hasn't changed.

Can anyone see what I might be doing wrong with the assignment between #2 and #3?

Upvotes: 1

Views: 1761

Answers (2)

Keith Langmead
Keith Langmead

Reputation: 1157

Just for completeness, the working code once I’d implemented Tomalak’s answer is.

$newstring = "mystring"
$bar = Get-Gpo -Name "My GPO"
$gpoid = $bar.Id.ToString()
$drivesxml = New-Object XML
$drivesxml.Load("\\mydomain.co.uk\sysvol\mydomain.co.uk\Policies\{gpoid}\User\Preferences\Drives\drives.xml")
$path=$drivesxml.SelectNodes('/Drives/Drive/Properties')    
$path[0].path = $path[0].path -replace 'Oldstring$',$newstring
$path[0].label = $path[0].label -replace '^Oldstring',$newstring
$drivesxml.Save("\\mydomain.co.uk\sysvol\mydomain.co.uk\Policies\{gpoid}\User\Preferences\Drives\drives.xml")

And the XML that it's querying for reference :

<?xml version="1.0" encoding="utf-8"?>
<Drives clsid="{8FDDCC1A-0C3C-43cd-A6B4-71A6DF30DA8C}">
  <Drive clsid="{935D1B74-9CB8-4e3c-9914-7DD559C7A417}" name="S:" status="S:" image="0" changed="2010-07-15 15:01:51" uid="{ACCCC2A9-809B-4CE3-9F2D-F4B4643B02F5}" bypassErrors="1">
    <Properties action="C" thisDrive="SHOW" allDrives="SHOW" userName="" path="\\mydomain.co.uk\GroupData$\Companies\mystring"     label="mystring - Shared drive" persistent="1" useLetter="1" letter="S" />
  </Drive>
  <Drive clsid="{935D1B74-9CB8-4e3c-9914-7DD559B8A417}" name="U:" status="U:" image="0" changed="2010-07-15 15:04:20" uid="{FA3F7F3C-A1A0-4618-89E1-88191C780A67}" bypassErrors="1">
    <Properties action="C" thisDrive="SHOW" allDrives="NOCHANGE" userName="" path="\\mydomain.co.uk\data\users\%username%" label="Personal drive (My documents)" persistent="1" useLetter="1" letter="U" />
  </Drive>
</Drives>

Upvotes: 1

Tomalak
Tomalak

Reputation: 338118

Powershell supports a "dot-path" syntax for navigating XML files that is very handy for reading data from an XML file—presumably to make it convenient to consume XML input or to use an XML config file.

The downside of that syntax is that it tries to return strings and arrays whenever it can. In your case, $drivesxml.Drives.Drive.Properties.Path[0] is a string, and not an XML node anymore, so whatever you assign to this value will be lost.

The trick is to stay with XML nodes. The easiest way to make this happen is to use XPath for navigation (it's more powerful than the dot-path syntax anyway):

$path = $drivesxml.SelectSingleNode('/Drives/Drive/Properties/Path')
$path.InnerText = $path.InnerText -replace "Oldstring$",$newstring

# ...

$drivesxml.Save($xmlPath)

You can also use .SelectNodes and a foreach loop to do multiple changes.


On an unrelated note, Powershell will do variable interpolation in double-quoted strings. This can clash with regular expressions, where the $ has its own meaning. In the case above there is no ambiguity, but it's better to get into the habit of using single-quoted strings for regular expressions:

$path.InnerText = $path.InnerText -replace 'Oldstring$',$newstring

A note on XML namespaces: If your XML file uses namespaces (declared by xmlns on the elements), you need to make those namespaces known before you can use XPath: Using PowerShell, how do I add multiple namespaces (one of which is the default namespace)?

Upvotes: 3

Related Questions