在 powershell 脚本中使用正则表达式替换 key/value 标签的值

using regex in powershell script to replace value of a key/value tags

使用 powershell 脚本,我需要更新 xml 文件中的一些值。 xml 是键和值的列表。

更具体地说,我需要查找密钥 "servername" 并使用 $myservername 更新相关值标签。还要查找密钥 "serverport" 并使用 $myport.

更新相关值标签

如何使用 powershell 脚本完成此操作?

我想到使用简单的正则表达式替换命令如下

$myservername = "some-server"
$myport = "8888"
$localFile = "C:\_Temp\Files190822115503\Manifest.xml"
$content = (Get-Content $localFile) 


$newcontent = $content -replace '"<Key>servername</Key>\s*<Value>(value1)</Value>"', $myservername


$newcontent = $content -replace '"<Key>serverport</Key>\s*<Value>(value1)</Value>"', $myport

以下只是 xml 文件的一部分:

    <Properties> 
       <ManifestProperty ValueType="string">
         <Key>servername</Key>
         <Value>value1</Value>
       </ManifestProperty>
       <ManifestProperty ValueType="string">
         <Key>serverport</Key>
         <Value>value2</Value>
       </ManifestProperty>
    </Properties>

我们需要搜索 "key" 然后替换 "value"

如果将文件作为 XmlDocument 对象读入 PowerShell,您可以执行以下操作:

$myservername = "some-server"
$myport = "8888"
$xml = [xml](Get-Content C:\temp\xml.xml)
$xml.SelectNodes("//Properties/ManifestProperty[Key = 'servername']") |
    Foreach-Object {
        $_.Value = $myservername
    }
$xml.SelectNodes("//Properties/ManifestProperty[Key = 'serverport']") |
    Foreach-Object {
    $_.Value = $myport
    }
$xml.Save("C:\Temp\xml.xml")

访问 XML 对象的属性将比正则表达式替换更可靠。

解释:

代码使用 [xml] 类型加速器将 C:\temp\xml.xml 的内容转换为 XmlDocument 对象。 SelectNodes() 方法使用 XPath 查找 Properties 的任何子元素,其中 ManifestProperty 元素包含 Key 节点。该节点必须包含文本 servername 或文本 serverport。以防万一该方法 returns 多个节点,结果将通过管道传输到 Foreach-Object 以更新所有值。

  • //Properties/ManifestProperty[Key = 'servername'] 查找属于 Properties 元素子元素且至少包含一个名为 Key 且值为 [=19 的子元素的所有 ManifestProperty 元素=].

如果你必须使用正则表达式来替换文本字符串,我不建议这样做,你可以执行以下操作:

$myservername = "some-server"
$myport = "8888"
$xml = Get-Content xml.xml -Raw
$xml = $xml -replace "(?s)(?<=<Key>servername</Key>.*?<Value>)value1(?=</Value>)",$myservername
$xml = $xml -replace "(?s)(?<=<Key>serverport</Key>.*?<Value>)value2(?=</Value>)",$myport

正则表达式替换说明:

  • Get-Content xml.xml -Raw:将文件内容作为一个字符串读取。这很有用,因为我们需要匹配多行。删除 -Raw 开关,将文件作为数组读入 PowerShell。对于数组输出,我们不能使用正则表达式来读后读或读前读。
  • (?s):这是单行修饰符。它允许正则表达式 . 匹配任何换行符。这对于跨多行匹配特别有用。
  • (?<=<Key>servername</Key>.*?<Value>):对字面匹配<Key>servername</Key>执行肯定的lookbehind断言(?<=),非贪婪匹配0个或多个字符,字面匹配<Value>.
  • value1:文字匹配
  • (?=</Value>):对文字匹配执行肯定的先行断言</Value>

下面是使用捕获组进行正则表达式替换的另一种方法:

$myservername = "some-server"
$myport = "8888"
$xml = Get-Content xml.xml -Raw
$xml = $xml -replace "(?s)(?<NameKey><Key>servername</Key>.*?<Value>)value1(?<NameValue></Value>)","`${NameKey}$myservername`${NameValue}"
$xml = $xml -replace "(?s)(?<PortKey><Key>serverport</Key>.*?<Value>)value2(?<PortValue></Value>)","`${PortKey}$myport`${PortValue}"

正则表达式替换说明:

  • Get-Content xml.xml -Raw:将文件内容作为一个字符串读取。这很有用,因为我们需要匹配多行。删除 -Raw 开关,将文件作为数组读入 PowerShell。对于数组输出,我们不能使用正则表达式来读后读或读前读。
  • (?s):这是单行修饰符。它允许正则表达式 . 匹配任何换行符。这对于跨多行匹配特别有用。
  • (?<PortKey><Key>serverport</Key>.*?<Value>)(?<PortKey>) 创建一个名为 PortKey 的捕获组。括号内匹配的所有内容都将包含在该捕获中。 serverport 字面匹配。 .*? 是0个或多个字符的惰性匹配。 <Value> 字面匹配。
  • value2:文字匹配
  • (?<PortValue></Value>):为括号内匹配的所有内容捕获名为 PortValue 的组。 </Value> 是文字匹配。
  • "`${PortKey}$myport`${PortValue}":这是替换字符串。请注意表达式周围的双引号,以便对 $myport 进行插值。 ${PortKey}${PortValue} 是访问捕获组所需的语法。反引号字符用于转义那些捕获组引用中的 $,因为我们不希望 PowerShell 插入它们。

您通常不需要命名您的捕获组。但是,由于其中一个捕获是数字的,这可能会导致访问默认捕获组时出现问题。 </code> 将访问第一个捕获组。由于 <code>$myport 是一个数字,因此 `$myport 将被计算为 888 并且正则表达式匹配将没有名为 18888 的捕获组。所以命名捕获组,增加了更好的可预测性。