Contents

在 Linux 命令行下评估 XPath 表达式

1. 概述

当我们使用XML 时,我们可以使用XPath 来浏览 XML 文档中的元素和属性。 在本教程中,我们将讨论如何在 Linux 命令行下计算 XPath 表达式。

2. 我们的 XML 示例和 XPath 表达式

首先,让我们创建一个 XML 文档books.xml,作为我们将在本教程中使用的输入 XML 文件:

<books>
    <book id="1" category="linux">
        <title lang="en">Linux Device Drivers</title>
        <year>2003</year>
        <author>Jonathan Corbet</author>
        <author>Alessandro Rubini</author>
    </book>
    <book id="2" category="linux">
        <title lang="en">Understanding the Linux Kernel</title>
        <year>2005</year>
        <author>Daniel P. Bovet</author>
        <author>Marco Cesati</author>
    </book>
    <book id="3" category="novel">
        <title lang="en">A Game of Thrones</title>
        <year>2013</year>
        <author>George R. R. Martin</author>
    </book>
    <book id="4" category="novel">
        <title lang="fr">The Little Prince</title>
        <year>1990</year>
        <author>Antoine de Saint-Exupéry</author>
    </book>
</books>

在我们的books.xml文件中,我们有四本书。稍后,我们将讨论如何在 Linux 命令行下评估几个 XPath 表达式:

  • //title[@lang=‘fr’] – 这个 XPath 表达式选择所有用法语编写的书籍的标题元素(“ book ”元素有一个“ lang ”属性,值为“ fr ”)
  • //book[year>2004]/title – 如果出版年份晚于 2004 年,此 XPath 表达式将选择所有书名元素(“ year ”元素的值大于2004

在本教程中,我们将讨论在命令行下使用 XPath 的三种不同方法:

3. 使用xmllint命令

xmllint命令与xmllib2 软件包*一起安装。 *通常,我们可以使用此命令来验证 XML 文件、解析 XML 文件或漂亮地打印 XML 文件。

xmllint命令支持“ -xpath  *”*选项来评估 XPath 表达式:

xmllint --xpath "XPATH_EXPRESSION" INPUT.xml

值得一提的是,由于 xmllib2仅实现XPath 1.0xmllint命令仅支持 XPath 1.0。

让我们用我们的 XPath 表达式进行测试,看看我们是否能得到预期的结果。

首先,让我们在books.xml中选择所有英文书籍的标题元素:

$ xmllint --xpath "//title[@lang='fr']" books.xml
<title lang="fr">The Little Prince</title>

我们在输出中得到了《小王子》这本书的标题元素。这是正确的,因为它是唯一具有*lang=”fr”*属性的标题元素。

其次,让我们测试另一个 XPath 表达式:

$ xmllint --xpath "//book[year>2004]/title" books.xml 
<title lang="en">Understanding the Linux Kernel</title>
<title lang="en">A Game of Thrones</title>

这一次,xmllint打印两个标题元素。我们的第二个 XPath 表达式也由xmllint命令正确评估。

4. 使用 XMLStarlet 工具包

XMLStarlet 是一个基于libxml2 的强大的命令行 XML 工具包。因此,与xmllint命令类似,XMLStartlet仅支持 XPath 1.0。

XMLStartlet 附带一个名为 xml的可执行文件,我们可以将其用作 xmlstarlet命令的缩写形式。

4.1.XMLStarlet 语法

xml命令的语法是:

xml [options] <command> [command options]

XMLStarlet 定义了一组命令来执行不同的 XML 操作——例如,ed ( edit ) 编辑或更新 XML 文档,  tr ( transform ) 使用 XSLT 转换 XML 文档,等等。

**要使用 XPath 选择数据或查询 XML 文档,我们可以使用sel ( select ) 命令。**事实上,  sel命令可以做的远不止 XPath 表达式求值。

基本上,sel命令允许我们避免编写 XSLT 样式表来执行一些 XML 文档查询。它可以通过命令行选项的组合为我们生成 XSLT。

也就是说,当我们使用 sel命令时,XMLStarlet 会将我们所有的命令参数转换为 XSLT 来对输入的 XML 文档进行查询。

让我们看一下sel命令的一般语法:

xml sel -t <template options> Input.xml

XSLT 模板 是 XSLT 的一个基本概念。使用sel命令,我们使用-t*选项创建模板

在本教程中,我们不会深入研究 XSLT 转换。我们的目标是评估 XPath 表达式。

sel命令支持许多模板选项。我们将介绍其中的两个:-c和*-v*,因为这两个模板选项非常常用于 XPath 评估。

例如,假设 XPath 表达式的评估结果是*text*:

  • -c “ XPath_Expression”选项将应用“ xsl:copy-of ” - 这会创建找到节点的副本,因此我们将获得:text
  • -v “ *XPath_Expression”*选项将应用“ xsl:value-of ”——这会在结果中提取 XML 元素的值,所以我们将有:text

4.2. 使用xml sel命令评估 XPath 表达式

现在,让我们尝试使用我们的两个 XPath 表达式。

首先,我们将使用带有*-c模板选项的xml sel*命令测试我们的第一个 XPath 表达式:

$ xml sel -t -c "//title[@lang='fr']" books.xml
<title lang="fr">The Little Prince</title>

如输出所示,我们的 XPath 表达式已被正确评估,并且我们得到了预期的title元素。 接下来,让我们看看如果我们使用 -v模板选项会得到什么:

$ xml sel -t -v "//title[@lang='fr']" books.xml
The Little Prince

这一次,我们得到了没有 XML 标记的title元素的文本。

现在,让我们用我们的其他 XPath 表达式测试该命令:

$ xml sel -t -c "/books/book[year>2004]/title" books.xml
<title lang="en">Understanding the Linux Kernel</title><title lang="en">A Game of Thrones</title>

当我们使用*-c选项时,输出包含两个预期的title*元素。

但是,输出不是“漂亮的”XML元素之间的换行符不知何故被吞没了。

发生这种情况是因为元素之间的换行符被视为空格,这意味着**<xsl:copy-of>指令将删除元素之间的所有空格**。

接下来,让我们看看使用*-v*选项会得到什么:

$ xml sel -t -v "/books/book[year>2004]/title" books.xml
Understanding the Linux Kernel
A Game of Thrones

如输出所示,当我们使用 -v选项时,我们将获得匹配元素的文本,每个值位于单独的行中。

这一次,不删除换行符。这是因为当结果有多个元素时,*xsl:value-of 将位于xsl:for-each *元素中,类似于:

<xsl:for-each select="/books/book[year&gt;2004]/title">
    <xsl:value-of select="."/>
</xsl:for-each>

因此,每个匹配元素的文本将打印到单独的行。

5. 使用xidel命令

xidel 命令是一个很好的 XML/HTML/JSON 数据提取实用程序,并且支持XPath 3.0

使用带有 XPath 表达式的xidel命令提取数据 非常简单:

xidel [options] --xpath "XPath Expression" XML_INPUT

我们可以传递一些选项来控制输出,我们将在后面的示例中看到。

5.1. 使用 XPath 表达式提取数据

让我们尝试使用第一个 XPath 表达式的xidel命令:

$ xidel --xpath "//title[@lang='fr']" books.xml 
 Retrieving: books.xml 
 Processing: books.xml 
The Little Prince

正如我们在输出中看到的,xidel 默认打印状态信息。此外,它会自动从找到的元素中提取文本。

如果我们想跳过状态消息,我们可以添加 -s选项让xidel在“静默”模式下工作

此外,我们可以 通过传递–printed-node-format=”xml”选项让xidel*打印完整的 XML 元素*:

/uploads/evaluate_xpath/1.png

上面的截图显示了xidel命令的一个很好的特性:当xidel输出为 XML 格式时,它会突出显示控制台输出中的属性。

接下来,让我们使用第二个 XPath 表达式执行xidel命令:

$ xidel -s --printed-node-format="xml" --xpath "/books/book[year>2004]/title" books.xml
<title lang="en">Understanding the Linux Kernel</title>
<title lang="en">A Game of Thrones</title>

正如我们所料,它从我们的示例文件中打印出两个标题元素。

5.2. 评估 XPath 3.0 表达式

最后,让我们测试一下 xidel命令是否可以与 XPath 3.0 表达式一起使用。 序列数据类型 从XPath 3.0 开始就出现了。因此,如果图书的出版年份在给定的值序列中,我们将使用序列数据类型编写 XPath 表达式来打印图书元素://book[year=(2004, 2005, 2013, 2020)]

让我们看看xidel是否可以评估这个 XPath 表达式并找到我们感兴趣的书籍:

$ xidel -s --printed-node-format="xml" --xpath "//book[year=(2004, 2005, 2013, 2020)]" books.xml
<book id="2" category="linux">
        <title lang="en">Understanding the Linux Kernel</title>
        <year>2005</year>
        <author>Daniel P. Bovet</author>
        <author>Marco Cesati</author>
    </book>
<book id="3" category="novel">
        <title lang="en">A Game of Thrones</title>
        <year>2013</year>
        <author>George R. R. Martin</author>
    </book>

太好了,它适用于 XPath 3.0 表达式!

由于xmllint和 XMLStarlet 仅支持 XPath 1.0,它们无法计算此 XPath 表达式:

$ xmllint --xpath "//book[year=(2004, 2005, 2013, 2020)]" books.xml
XPath error : Invalid expression
//book[year=(2004, 2005, 2013, 2020)]
                 ^
XPath evaluation failure
$ xml sel -t -c "//book[year=(2004, 2005, 2013, 2020)]" books.xml
Invalid expression: //book[year=(2004, 2005, 2013, 2020)]
compilation error: element copy-of
xsl:copy-of : could not compile select expression '//book[year=(2004, 2005, 2013, 2020)]'