Bash - 如果子节点的属性值不等于特定值,则删除 XML 个节点?
Bash - Remove XML nodes if the attribute value of a child node does not equal a specific value?
我有 RSS 提要,如下所示:
<?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
.
如何使用 Bash 删除值从指定符号开始的 XML 节点?
Bash 特征本身不太适合解析 XML.
这位著名的 Bash FAQ 声明如下:
Do not attempt [to extract data from an XML file] with sed, awk, grep, and so on (it leads to undesired results).
如果您还没有安装 XML Starlet,请考虑使用 XML 特定的命令行工具,例如 XMLStarlet. See download info 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
- Edit/Update XML 文档。
-N x="http://www.w3.org/2005/Atom"
- -N
选项将命名空间(即 http://www.w3.org/2005/Atom
)绑定到我们“任意命名为 x
.
-d
- 删除匹配的节点。
'//x:entry[not(child::x:link[@href="https://myhomesite.com"])]'
xpath 表达式用于 find/match 您问题中指定的适当节点。
all nodes (/feed/entry) where link href != http://myhomesite.com
.
如您所见,在 XPath 表达式中,我们在元素节点名称前加上 x
前缀,即 x:entry
和 x:link
以确保我们以正确的方式寻址元素命名空间。
/path/to/file.rss
- 源 .rss
文件的路径名。
保存结果 XML (RSS)
要保存结果 XML 您可以:
将 --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
或者,使用 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
应该替换为你想要保存的真实路径名新文件。
XPath 与 local-name()
:
鉴于您的示例源 XML (RSS) 不包含任何 QNames it's also possible to utilize XPath's 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
部分201=] 改为 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 作者随后在评论中写道:
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]
something like this.. 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
我有 RSS 提要,如下所示:
<?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
.
如何使用 Bash 删除值从指定符号开始的 XML 节点?
Bash 特征本身不太适合解析 XML.
这位著名的 Bash FAQ 声明如下:
Do not attempt [to extract data from an XML file] with sed, awk, grep, and so on (it leads to undesired results).
如果您还没有安装 XML Starlet,请考虑使用 XML 特定的命令行工具,例如 XMLStarlet. See download info 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
- Edit/Update XML 文档。-N x="http://www.w3.org/2005/Atom"
--N
选项将命名空间(即http://www.w3.org/2005/Atom
)绑定到我们“任意命名为x
.-d
- 删除匹配的节点。'//x:entry[not(child::x:link[@href="https://myhomesite.com"])]'
xpath 表达式用于 find/match 您问题中指定的适当节点。all nodes (/feed/entry) where link href !=
http://myhomesite.com
.如您所见,在 XPath 表达式中,我们在元素节点名称前加上
x
前缀,即x:entry
和x:link
以确保我们以正确的方式寻址元素命名空间。/path/to/file.rss
- 源.rss
文件的路径名。
保存结果 XML (RSS)
要保存结果 XML 您可以:
将
--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
或者,使用 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
应该替换为你想要保存的真实路径名新文件。
XPath 与 local-name()
:
鉴于您的示例源 XML (RSS) 不包含任何 QNames it's also possible to utilize XPath's 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
部分201=] 改为 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 作者随后在评论中写道:
One question more. Condition
[not(child::_:link[@href="myhomesite.com"])]
is strict. I wanna be something like start withmyhomesite.com
but URI not important i.e.myhomesite.com**anything**
. It's possible? [sic]something like this..
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