Bash - 如果子节点的属性值不等于特定值,则删除 XML 节点? [英] Bash - Remove XML nodes if the attribute value of a child node does not equal a specific value?

查看:43
本文介绍了Bash - 如果子节点的属性值不等于特定值,则删除 XML 节点?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有 RSS 提要,如下所示:

I have RSS feed, like this:

<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>my feed</title>
  <link rel="self" href="http://myhomesite.com/articles/feed/"/>
  <updated>2019-11-04T12:45:00Z</updated>
  <id>http://myhomesite.com/articles/feed/?dt=2019-11-04T12:45:00Z</id>
  <entry>
    <id>id0</id>
    <link rel="alternate" type="text/html" href="https://yandex.ru/link123"/>
    <author>
      <name/>
    </author>
    <published>2019-11-04T12:45:00Z</published>
    <updated>2019-11-04T12:45:00Z</updated>
    <title type="html"><![CDATA[foo bar foo bar]]></title>
    <content type="html"><![CDATA[]]></content>
  </entry>
  <entry>
    <id>id2</id>
    <link rel="alternate" type="text/html" href="https://myhomesite.com"/>
    <author>
      <name/>
    </author>
    <published>2019-11-04T09:45:00Z</published>
    <updated>2019-11-04T09:45:00Z</updated>
    <title type="html"><![CDATA[foo bar foo bar]]></title>
    <content type="html"><![CDATA[]]></content>
  </entry>
....

我想删除所有节点 (/feed/entry),其中 link href != http://myhomesite.com.

I want remove all nodes (/feed/entry) where link href != http://myhomesite.com.

如何使用 Bash 删除值从指定符号开始的 XML 节点?

How do I remove XML node where value start at specified symbols using Bash?

推荐答案

Bash 特性本身并不是很适合解析 XML.

Bash features by themselves are not very well suited parsing XML.

这个著名的 Bash FAQ 陈述如下:

This renowned Bash FAQ states the following:

不要尝试使用 , , 等等(它导致 不希望的结果).

Do not attempt [to extract data from an XML file] with sed, awk, grep, and so on (it leads to undesired results).

考虑使用特定于 XML 的命令行工具,例如 XMLStarlet.如果您尚未安装 XML Starlet,请参阅此处的下载信息.

Consider utilizing an XML specific command line tool, such as XMLStarlet. See download info here if you don't already have XML Starlet installed.

使用 XML Starlet,您可以运行以下命令将所需的结果输出到您的终端:

Using XML Starlet you can run the following command to output the desired results to your terminal:

xml ed -N x="http://www.w3.org/2005/Atom" -d '//x:entry[not(child::x:link[@href="https://myhomesite.com"])]' /path/to/file.rss

注意:上面显示的命令末尾的 /path/to/file.rss 部分应替换为实际路径名.rss 文件.

Note: The /path/to/file.rss part at the end of the command shown above should be substituted with the real pathname to the actual .rss file.

说明:

上述命令的部分分解如下:

The parts of the aforementioned command breakdown as follows:

  • xml - 调用 XML Starlet 命令.

  • xml - invoke the XML Starlet command.

ed - 编辑/更新 XML 文档.

ed - Edit/Update the XML document.

-N x=http://www.w3.org/2005/Atom" - -N 选项绑定命名空间,即http://www.w3.org/2005/Atom,到我们任意命名为 x 的前缀.

-N x="http://www.w3.org/2005/Atom" - The -N option binds the namespace, i.e. http://www.w3.org/2005/Atom, to a prefix that we've arbitrarily named x.

-d - 删除匹配的节点.

-d - delete node(s) that are matched.

'//x:entry[not(child::x:link[@href="https://myhomesite.com"])]' 用于查找/匹配的 表达式问题中指定的适当节点.

'//x:entry[not(child::x:link[@href="https://myhomesite.com"])]' The xpath expression used to find/match the appropriate nodes as specified in your question.

链接 href != http://myhomesite.com 的所有节点 (/feed/entry).

all nodes (/feed/entry) where link href != http://myhomesite.com.

如您所见,在 XPath 表达式中,我们在元素节点名称前添加了 x 前缀,即 x:entryx:link 以确保我们处理正确命名空间中的元素.

As you can see, in the XPath expression we prepend the x prefix to the element node names, i.e. x:entry and x:link to ensure we address the elements in the correct namespace.

/path/to/file.rss - 源 .rss 文件的路径名.

要保存结果 XML,您可以:

To save the resultant XML you can either:

  1. --inplace 选项添加到上述命令中 - 这将使用所需的结果覆盖原始 .rss.例如:

  1. Add the --inplace option to the aforementioned command - this will overwrite the original .rss with the desired result. For instance:

 xml ed --inplace -N x="http://www.w3.org/2005/Atom" -d '//x:entry[not(child::x:link[@href="https://myhomesite.com"])]' /path/to/file.rss

  • 或者,使用重定向运算符(>) 并指定保存输出位置的路径名.例如,以下复合命令会将结果保存到一个新文件中:

  • Or, utilize the redirection operator (>) and specify a pathname to the the location at which to save the output. For instance the following compound command will save the results to a new file:

     xml ed -N x="http://www.w3.org/2005/Atom" -d '//x:entry[not(child::x:link[@href="https://myhomesite.com"])]' /path/to/file.rss > /path/to/results.rss
    

    注意:上述复合命令末尾的 /path/to/results.rss 应替换为您想要的真实路径名保存新文件.

    Note: The /path/to/results.rss at the end of the aforementioned compound command should be substituted with a real pathname to where you want to save the new file.

    带有 local-name() 的 XPath:

    鉴于您的示例源 XML (RSS) 不包含任何 QNames 也可以使用 XPath 的 local-name() 函数.这将不需要使用 XMLStarlet 的 -N 选项绑定命名空间.例如:

    XPath with local-name():

    Given that your example source XML (RSS) does not include any QNames it's also possible to utilize XPath's local-name() function. This will negate the need to bind the namespace using XMLStarlet's -N option. For example:

    xml ed -d '//*[local-name() = "entry" and not(child::*[local-name() = "link"][@href="https://myhomesite.com"])]' /path/to/file.rss
    


    重要提示:可能需要用 xmlstarlet<替换本文中显示的所有示例命令中的前导 xml 部分/code> 代替.例如:


    IMPORTANT: You may need to substitute the leading xml part in all the example commands shown in this post with xmlstarlet instead. For example:

    xmlstarlet ed -N x="http://www.w3.org/2005/Atom" -d '//x:entry[not(child::x:link[@href="https://myhomesite.com"])]' /path/to/file.rss.
    ^^^^^^^^^^
    


    鉴于您的示例 XML,还可以使用默认命名空间的简化语法,即使用 _: 代替 x:.通过使用下划线 (_),您无需使用 -N 选项将命名空间绑定到前缀.请参阅标题为 1.3 的部分.有关此功能的更多信息,请参阅 XMLStarlet 文档中的更方便的解决方案.

    Given your example XML it's also possible to utilize a simplified syntax for the default namespace, which is to use _: instead x:. By using an underscore (_) you don't need to utilize the -N option to bind the namespace to a prefix. Refer to the section titled 1.3. A More Convenient Solution in the XMLStarlet documentation for further information regarding this feature.

    例如:

    xml ed -d '//_:entry[not(child::_:link[@href="https://myhomesite.com"])]' /path/to/file.rss
    

    当你的源 XML 使用命名空间时,为了进一步了解使用 XMLStarlet,我建议还阅读 命名空间和文档中的默认命名空间.

    To further understand using XMLStarlet when your source XML uses namespaces I suggest also reading Namespaces and default namespace in the documentation.

    编辑 2:

    OP 的作者随后在评论中写道:

    The author of the OP subsequently wrote the following in the comments:

    还有一个问题.条件 [not(child::_:link[@href="myhomesite.com"])] 是严格的.我想要以 myhomesite.com 开头,但 URI 并不重要,即 myhomesite.com**anything**.这是可能的?[原文]

    One question more. Condition [not(child::_:link[@href="myhomesite.com"])] is strict. I wanna be something like start with myhomesite.com but URI not important i.e. myhomesite.com**anything**. It's possible? [sic]

    像这样.. xmlstarlet ed -N x=http://www.w3.org/2005/Atom"-d '//x:entry[not(child::x:link[matches(@href, '^https://myhomesite.com/')]/@href)]' feed.rs

    考虑使用 Xpath 的 starts-with() 函数与前面给出的任何一个例子.例如:

    Consider utilizing Xpath's starts-with() Function with any one of the previously given examples. For example:

    • 使用 -N 选项和 starts-with():

    xml ed -N x="http://www.w3.org/2005/Atom" -d '//x:entry[not(child::x:link[starts-with(@href, "https://myhomesite.com")])]' file.rss
    

  • 使用local-name()starts-with():

    xml ed -d '//*[local-name() = "entry" and not(child::*[local-name() = "link"][starts-with(@href, "https://myhomesite.com")])]' file.rss
    

  • 使用默认命名空间的简化语法,即下划线和starts-with():

    xml ed -d '//_:entry[not(child::_:link[starts-with(@href, "https://myhomesite.com")])]' file.rss
    

  • 这篇关于Bash - 如果子节点的属性值不等于特定值,则删除 XML 节点?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆