【问题标题】:Conditional replace in XML filesXML 文件中的条件替换
【发布时间】:2016-11-26 20:40:36
【问题描述】:

我正在使用 PowerShell 递归替换 XML 文件中的文本。该脚本在替换时工作正常。然而,XML 文件也有不应被替换的文件路径。这是当前正在使用的脚本

if ( $content -match ' web site | web-site ' ) {
    $content -replace ' web site ',' New Site ' -replace ' web-site ',' New Site ' |
        Out-File $file.FullName -Encoding utf8

例如,如果 XML 文件有

<title>web site</title>
<subtitle>web-site</subtitle>
<path>c:/web site/website.xml</path>

预期的输出应该如下所示。文件路径中的匹配文本应被忽略。如果字符串在/web site//web-site.xml 之间,我如何添加一个条件来忽略字符串?

<title>New Site</title>
<subtitle>New Site</subtitle>
<path>c:/web site/website.xml</path>

【问题讨论】:

    标签: powershell powershell-2.0 powershell-3.0


    【解决方案1】:

    将 XML 作为 XML 处理通常效率更高,出错率也更低。选择要更新的节点,然后将修改后的数据保存回文件。

    $filename = 'C:\path\to\your.xml'
    
    [xml]$xml = Get-Content $filename
    $xml.SelectNodes('//*[self::title or self::subtitle]') |
        Where-Object { $_.'#text' -match 'web.site' } |
        ForEach-Object { $_.'#text' = 'New Site' }
    $xml.Save($filename)
    

    如果您需要修改节点文本的子字符串,您可以执行以下操作:

    $filename = 'C:\path\to\your.xml'
    
    [xml]$xml = Get-Content $filename
    $xml.SelectNodes('//*[self::title or self::subtitle]') |
        Where-Object { $_.'#text' -match 'web.site' } |
        ForEach-Object { $_.'#text' = $_.'#text' -replace 'web.site', 'New Site' }
    $xml.Save($filename)
    

    【讨论】:

    • 干得好;我建议使用-match 'web[ -]site' 来更紧密地匹配 OP 的代码;您使用self:: 有什么特别的原因吗?没有它似乎工作正常。
    • 我不知道它在没有 self 轴的情况下也能工作。
    • 是的,但问题是文件路径可以包含许多不同的 xml 标记,因此无法使用此选项。您知道我们是否可以像 /web site/ 之前和之后的 slach 一样以正则表达式的形式指定要忽略的上一个和最后一个文本
    • 如果您提供有代表性的样本作为输入和期望的输出,而不是“类似这样或其他,我希望解决方案是这样那样”,您会得到更好的答案。
    • 不要在未解析的 XML 上使用字符串替换。 EVER. 确定要更改(子)字符串的节点,然后相应地调整 XPath 表达式 (//*[nodeA or nodeB or nodeC or ...])。
    【解决方案2】:

    这是快速修复,但请注意更强大的解决方案将使用 PowerShell 的 XML 解析功能:请参阅 Ansgar Wiecher's helpful answer

    注意:
    - 此答案假定感兴趣的字符串与 XML 文档的句法元素不冲突,例如元素名称和属性名称(恰好适用于所讨论的 特定 字符串),这说明了原因使用真正的 XML 解析器是更好的选择。

    $content = @'
    <doc>
    <title>web site</title>
    <subtitle>web-site</subtitle>
    <path>c:/web site/website.xml</path>
    </doc>
    '@
    
    $modifiedContent = $content -replace '(^|[^/])web[ -]site([^/]|$)', '$1New Site$2'
    # Replace 'web site' and 'web-site' if not preceded or followed by a '/'.
    # Note: `web[ -]site` is the equivalent of `web site|web-site`
    
    if ($modifiedContent -cne $content) { # If contents have changed, save.
      Out-File -InputObject $modifiedContent $file.FullName -Encoding utf8
    }
    

    【讨论】:

    • @Ansgar 和 mklement 有时会在文本前后有空格,例如“ new web site”,这就是为什么我想知道是否有任何快速修复使用任何正则表达式忽略 /text/。是否有任何快速修复狐狸,或者你能给我一个在 XML 解析中处理这个问题的线索
    • @user2628187:请查看我的更新。 Ansgar 的回答专门针对 titlesubtitle 元素,所以我希望它能够按原样工作(除非您不想使用明确的元素名称列表)。
    猜你喜欢
    • 1970-01-01
    • 2014-09-29
    • 2021-01-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-08
    • 1970-01-01
    • 2014-09-07
    相关资源
    最近更新 更多