Skip to content

Instantly share code, notes, and snippets.

@spuder
Last active June 27, 2023 05:01
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save spuder/28c462e2e4eb651db5d2 to your computer and use it in GitHub Desktop.
Save spuder/28c462e2e4eb651db5d2 to your computer and use it in GitHub Desktop.
Powershell replace single xml line
# Based on this script https://ask.puppetlabs.com/question/4749/augeas-or-alternative-xml-modification-on-windows/
# I had to flip the logic if $node.NodeType -ne 'Element' to get it to work properly
[CmdletBinding()]
param (
[string] $filename = $(throw "filename is a required parameter"),
[string] $xpath = $(throw "xpath is a required parameter"),
[string] $value = $(throw "value is a required parameter")
)
write-verbose "Received arguments:"
write-verbose "filename: ""$filename"""
write-verbose "xpath: ""$xpath"""
write-verbose "value: ""$value""`n"
$xml = [xml](Get-Content $filename)
try {$nodes = $xml.SelectNodes($xpath)}
catch {Write-Verbose "I cound't find any nodes"; exit 1}
echo "I've got nodes" $nodes
if ($nodes.count -eq 0) {write-verbose "No match in XML file"}
foreach ($node in $nodes) {
Write-Verbose "Found $($node) with type $($node.NodeType) "
echo "node InnerXml is"$node.InnerXml
echo "node Value is "$node.Value
echo "node type is" $node.NodeType
if ($node -ne $null) {
if ($node.NodeType -ne "Element") {
Write-Verbose "Replaced InnerXml ""$($node.InnerXml)"" with ""$value"""
$node.InnerXml = $value
}
else {
Write-Verbose "Replaced Value ""$($node.Value)"" with ""$value"""
$node.Value = $value
}
}
}
$xml.save($filename)
powershell -executionpolicy remotesigned -file .\augeas.ps1 -f ".\foo.xml" -xpath "/configuration/herp/foo" -value "84" -verbose

You must wrap the path and xpath in doublequotes I find

<?xml version="1.0" encoding="utf-8"?>
<configuration>
<herp>
<foo value="42" />
</herp>
<woot />
</configuration>
# A script that manipulates xml documents just like augeas
# Written by spuder, borrowed parts from https://ask.puppetlabs.com/question/4749/augeas-or-alternative-xml-modification-on-windows/
# .\xmlModify.ps1 -filename "c:\foobar.xml" -xpath "/configuration/foo" -element "bacon" -attribute_key "isGood" -attribute_value "true" -text "I love bacon" -verbose
#<?xml version="1.0" encoding="utf-8"?>
#<configuration>
# <foo/>
# <bacon isGood="true">I love bacon</bacon>
[CmdletBinding()]
param (
[string] $filename = $(throw "filename is a required parameter"),
[string] $xpath = $(throw "xpath is a required parameter"),
[string] $element = $(throw "node is a required parameter"),
[string] $attribute_key = "",
[string] $attribute_value = "",
[string] $text = ""
)
write-verbose "Received arguments:"
write-verbose "filename: ""$filename"""
write-verbose "xpath: ""$xpath"""
write-verbose "element: ""$element"""
write-verbose "attribute_key: ""$attribute_key"""
write-verbose "attribute_value: ""$attribute_value"""
write-verbose "text: ""$text""`n"
# Get xml Document
$xml = [xml](Get-Content $filename)
# If path exists, but not node, create it
# If path and node don't exist, error and exit
if ($xml.SelectNodes($xpath) )
{
Write-Verbose "Found $filename $xpath"
if (-Not $xml.SelectSingleNode("$xpath/$element") )
{
Write-Host "$($element) does not exist in $($xpath), appending"
$child = $xml.CreateElement($element)
$xml.SelectSingleNode($xpath).AppendChild($child) > $null
}
}
else
{
echo "Could not find $($element) in $($filename)"
exit 1
}
# Update node value
$xmlItem = $xml.SelectSingleNode("$($xpath)/$($element)")
$xmlItem.SetAttribute($attribute_key, $attribute_value)
#Update text if provided
if ($($text) -ne "") {
$xmlItem.set_InnerXML("$text")
}
$xmlitem
# Overwrite file
$xml.save($filename)
@n360speed
Copy link

I thought I would share, I updated your script so it would take a hash. Comes in handy if you are updating optional parameters:

  • Edit misspelled attributes
  • Fixed formatting and comments
# A script that manipulates xml documents just like augeas
# Written by spuder, borrowed parts from https://ask.puppetlabs.com/question/4749/augeas-or-alternative-xml-modification-on-windows/
# .\xmlModify.ps1 -filename "c:\foobar.xml" -xpath "/configuration/foo" -element "bacon" -attribute_key "isGood" -attribute_value "true" -hash '{"redirectPort"=>"8443", "connectionTimeout"=>"20001"}' -text "I love bacon" -verbose

#<?xml version="1.0" encoding="utf-8"?>
#<configuration>
#  <foo/>
#    <bacon isGood="true" redirectPort="8843" connectionTimeout="2000">I love bacon</bacon>

[CmdletBinding()]

param (
[string] $filename          = $(throw "filename is a required parameter"),
[string] $xpath             = $(throw "xpath is a required parameter"),
[string] $element           = $(throw "node is a required parameter"),
[string] $attribute_key     = "",
[string] $attribute_value   = "",
[string] $hash              = "",
[string] $text              = ""
)

write-verbose "Received arguments:"
write-verbose "filename:        ""$filename"""
write-verbose "xpath:           ""$xpath"""
write-verbose "element:         ""$element"""
write-verbose "attribute_key:   ""$attribute_key"""
write-verbose "attribute_value: ""$attribute_value"""
write-verbose "hash:            ""$hash"""
write-verbose "text:            ""$text""`n"

# Get xml Document
$xml = [xml](Get-Content $filename)

# If path exists, but not node, create it
# If path and node don''t exist, error and exit
if ($xml.SelectNodes($xpath) )
{
    Write-Verbose "Found $filename $xpath"
    if (-Not $xml.SelectSingleNode("$xpath/$element") )
    {
        Write-Host "$($element) does not exist in $($xpath), appending"
        $child = $xml.CreateElement($element)
        $xml.SelectSingleNode($xpath).AppendChild($child) > $null
    }
}
else
{
    echo "Could not find $($element) in $($filename)"
    exit 1
}

# Update node value
$xmlItem = $xml.SelectSingleNode("$($xpath)/$($element)")
if($($attribute_key) -ne "") {
    $xmlItem.SetAttribute($attribute_key, $attribute_value)
}

# Update via hash
# where hash like {"redirectPort"=>"8443", "connectionTimeout"=>"20001"}
if ($($hash) -ne "") {
    # trim { and }
    $hash = $hash.Trim('{').Trim('}')
    $attributes = $hash.Split(",")
    foreach($attribute in $attributes) {
        $result = $attribute.Split('=>')
        write-verbose "result:            ""$result"""
        $key = $result[0].Trim().Trim("""")
        $value = $result[2].Trim().Trim("""")
        write-verbose "key:            ""$key"""
        write-verbose "value:          ""$value"""
        $xmlItem.SetAttribute($key, $value)
    }
}

#Update text if provided
if ($($text) -ne "") {
    $xmlItem.set_InnerXML("$text")
}

$xmlitem

# Overwrite file
$xml.save($filename)

@iamsofaking
Copy link

I added a snippet to create the node structure if they didn't exist.

$nodes = $xpath.Split("/")
$nodecheck = Select-XML -Xml $xml -XPath $xpath
$x = 0
Do {
$prenode = $node
$node = $node + "/" + $nodes[$x]
if ($xml.SelectSingleNode($node)){
write-host "$node exists!"
} else {
$child = $xml.CreateElement($nodes[$x])
$xml.SelectSingleNode($prenode).AppendChild($child) > $null
}
$x = $x+1
$nodecheck = Select-XML -Xml $xml -XPath $xpath
} Until ($nodecheck)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment