Bash-如果子节点的属性值不等于特定值,则删除XML节点? 保存生成的XML(RSS)带有local-name()的XPath:

我有RSS feed,例如:

<?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>
....

我要删除所有链接 href != /feed/entry的节点(http://myhomesite.com

如何使用Bash删除以指定符号开头的XML节点?

LISA900205 回答:Bash-如果子节点的属性值不等于特定值,则删除XML节点? 保存生成的XML(RSS)带有local-name()的XPath:

Bash功能本身不太适合解析XML。

此著名的Bash FAQ指出:

  

请勿不要使用等尝试 [从XML文件中提取数据] 上(它指向undesired results)。

考虑利用XML特定的命令行工具,例如XMLStarlet。如果尚未安装XML Starlet,请参阅下载信息here


解决方案:

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

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文件的真实路径名。

说明:

上述命令的各个部分如下:

  • xml -调用XML Starlet命令。
  • ed -编辑/更新XML文档。
  • -N x="http://www.w3.org/2005/Atom" --N选项将名称空间http://www.w3.org/2005/Atom绑定到我们任意命名为x的前缀。 / li>
  • -d -删除匹配的节点。
  • '//x:entry[not(child::x:link[@href="https://myhomesite.com"])]' 表达式用于查找/匹配问题中指定的适当节点。

      

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

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

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

保存生成的XML(RSS)

要保存生成的XML,您可以:

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

    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
    
  2. 或者,利用redirection operator>)并指定保存输出的位置的路径名。例如,以下复合命令会将结果保存到新文件中:

    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替换为要保存新文件的真实路径名。

带有local-name()的XPath:

鉴于您的示例源XML(RSS)不包含任何QNames,因此还可以利用XPath的local-name()函数。这将消除使用XMLStarlet的-N选项绑定名称空间的需要。例如:

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

重要提示::您可能需要 将本文中显示的所有示例命令中的前xml部分替换为xmlstarlet。例如:

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选项将名称空间绑定到前缀。有关此功能的更多信息,请参考XMLStarlet文档中标题为1.3. A More Convenient Solution的部分。

例如:

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

要进一步了解当源XML使用名称空间时如何使用XMLStarlet,我建议您还阅读文档中的Namespaces and default namespace


编辑2:

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

  

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

     

类似这样的东西。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()函数与前面给出的任何示例一起使用。例如:

  • 使用-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
    
本文链接:https://www.f2er.com/3161649.html

大家都在问