Linux中XMLLINT命令简介
1. 概述
在本教程中,我们将了解xmllint命令行工具。特别是,我们将通过示例用例了解xmllint在处理 XML 文件的上下文中提供的广泛功能。
2. xmllint
XML 是一种标记语言,被广泛用于通过网络构建和传输数据。虽然有大量的库和框架允许我们解析和处理 XML 文档,但 xmllint是 Linux 中最通用的 XML 命令行工具之一。
2.1. 安装
要 在基于 Debian 的 Linux 中 安装*xmllint * ,我们可以使用apt-get 安装libxml2-utils包:
$ sudo apt-get update -qq
$ sudo apt-get install -y libxml2-utils
另一方面,在基于 RHEL 的 Linux(例如 CentOS)中,我们需要 使用yum 安装xmlstarlet包 :
$ sudo yum update -qq
$ sudo yum install -y xmlstarlet
2.2. 一般语法
通常,我们在运行 xmllint命令时带有一系列可选标志和一个或多个 XML 文件路径:
xmllint [options] xml_file_1 xml_file_2 ...
让我们看一下xmllint的一些用例。
3.解析和格式化XML
3.1.解析和快速验证
当我们 在没有任何选项的 XML 文件上 运行xmllint 时,xmllint将简单地解析文件并将内容显示到标准输出。如果解析成功并且内容显示在标准输出上没有任何错误,我们就可以确保 XML 文件格式正确。因此,我们可以使用xmllint作为一种快速验证 XML 文档是否损坏的方法。
例如,当我们 在现有的notebook.xml XML 文件上运行xmllint时,我们将看到解析后的内容:
$ xmllint laptop.xml
<?xml version="1.0"?>
<specification>
<type>laptop</type>
<model>macbook</model>
<screenSizeInch>15</screenSizeInch>
</specification>
我们还可以添加 –noout选项来禁止 xmllint将 XML 文件的内容打印到标准输出:
$ xmllint --noout laptop.xml
$ echo $?
0
**另一方面, 如果 XML 文件格式错误, xmllint将返回错误。**为了演示这个场景,让我们将 laptop.xml复制到一个单独的文件laptop-malformed.xml 中。然后,我们删除screenSizeInch的结束标记以模拟格式错误的 XML:
$ cat laptop-malformed.xml
<specification>
<type>laptop</type>
<model>macbook</model>
<screenSizeInch>15
</specification>
现在,当我们在laptop- malformed.xml 上运行xmllint命令时 ,会报错:
$ xmllint --noout laptop-malformed.xml
laptop-malformed.xml:5: parser error : Opening and ending tag mismatch: screenSizeInch line 4 and specification
</specification>
^
laptop-malformed.xml:6: parser error : EndTag: '</' not found
^
$ echo $?
1
正如预期的那样, xmllint抱怨我们在 XML 文件中删除了缺少的结束标记。
3.2. 美化 XML
要格式化和美化 XML 文档,我们可以 使用–format参数运行xmllint。**例如,假设我们的 laptop-unformatted.xml 未格式化且未缩进:
$ cat laptop-unformatted.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE specification SYSTEM "laptop.dtd"><specification>
<type>laptop</type><model>macbook</model><screenSizeInch>15</screenSizeInch></specification>
我们可以使用xmllint重新格式化文档,使其更具可读性 :
$ xmllint --format laptop-unformatted.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE specification SYSTEM "laptop.dtd">
<specification>
<type>laptop</type>
<model>macbook</model>
<screenSizeInch>15</screenSizeInch>
</specification>
此外,我们可以通过设置**环境变量XMLLINT_INDENT来更改缩进字符。**例如,我们可以用 8 个空格缩进来重新格式化 XML 文档,而不是两个空格。
为此,我们首先设置XMLLINT_INDENT环境变量:
$ export XMLLINT_INDENT=" "
然后,命令 xmllint –format将格式化 XML 文档,缩进为 8 个空格:
$ xmllint --format laptop-unformatted.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE specification SYSTEM "laptop.dtd">
<specification>
<type>laptop</type>
<model>macbook</model>
<screenSizeInch>15</screenSizeInch>
</specification>
3.3. 删除可忽略的空格
为了保持 XML 文档较小,我们可以使用带有 –noblanks 参数的xmllint删除缩进空格和换行符:
$ cat laptop.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE specification SYSTEM "laptop.dtd">
<specification>
<type>laptop</type>
<model>macbook</model>
<screenSizeInch>15</screenSizeInch>
</specification>
$ xmllint --noblanks laptop.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE specification SYSTEM "laptop.dtd">
<specification><type>laptop</type><model>macbook</model><screenSizeInch>15</screenSizeInch></specification>
3.4. 从输出中删除 DTD
要从 XML 中删除 DTD,我们可以 使用–dropdtd选项运行xmllint :**
$ cat laptop-w-dtd.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE specification [
<!ELEMENT specification (type,model,screenSizeInch,hasBluetooth)>
<!ELEMENT type (#PCDATA)>
<!ELEMENT model (#PCDATA)>
<!ELEMENT screenSizeInch (#PCDATA)>
<!ELEMENT hasBluetooth (#PCDATA)>
]>
<specification>
<type>laptop</type>
<model>macbook</model>
<screenSizeInch>15</screenSizeInch>
</specification>
$ xmllint --dropdtd laptop-w-dtd.xml
<?xml version="1.0" encoding="UTF-8"?>
<specification>
<type>laptop</type>
<model>macbook</model>
<screenSizeInch>15</screenSizeInch>
</specification>
当我们只想将 XML 文档保存到没有 DTD 的单独文件中时,此选项可能会很有帮助。
4. 验证 XML
在 XML 中,可以执行两种不同类型的验证:文档类型定义 (DTD) 和 XML 模式定义 (XSD)。鉴于两种基于模式的验证的普遍性, xmllint 通过选项*–valid*、 –dtdvalid和*–schema*支持这两种验证方法 。
4.1. 根据文档类型定义 (DTD) 进行验证
文档类型定义 (DTD) 是定义构成有效 XML 文档的定义文档。它通过确保其符合预定义的结构来补充其目标 XML 文档。例如,下面是一个可能的 XML DTD 文档:
$ cat laptop.dtd
<!ELEMENT specification (type,model,screenSizeInch)>
<!ELEMENT type (#PCDATA)>
<!ELEMENT model (#PCDATA)>
<!ELEMENT screenSizeInch (#PCDATA)>
在 DTD 中,我们可以看到所有节点的定义,例如specification、type、model和screenSizeInch。首先,我们看到specification节点的定义是它必须包含所有的子节点。随后,在每个子节点中,我们也看到它们是一种可解析字符数据(PCDATA)。
使用xmllint,我们可以根据 DTD 验证我们的 XML 文档,以验证 XML 文档的有效性。
*对于 XML 本身内部的 DTD,对它们进行验证可以像使用 xmllint 传递–valid选项一样 简单。*例如,我们可以单独使用–valid选项验证laptop-bluetooth.xml ,因为DTD 已经在XML 本身中:
$ xmllint --noout --valid laptop-bluetooth.xml
laptop-w-dtd.xml:13: element specification: validity error : Element specification content does not follow the DTD, expecting (type , model , screenSizeInch , hasBluetooth), got (type model screenSizeInch )
</specification>
^
*要在单独的文件中验证 DTD,我们可以使用–dtdvalid选项,后跟 DTD 的文件路径。例如,我们可以 使用 *xmllint验证laptop.xml与 laptop.dtd:
$ xmllint --noout --dtdvalid ./laptop.dtd laptop.xml
4.2. 根据 XML 模式定义 (XSD) 进行验证
与 DTD 类似,XML 模式定义 (XSD) 定义 XML 文档的结构。但是,XSD 也允许我们定义不同子节点的数据类型,甚至限制节点中数据的长度,这是 DTD 做不到的。对于我们的 laptop.xml,XSD 的一个例子可能是 notebook.xsd:
$ cat laptop.xsd
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="https://www.w3schools.com"
xmlns="https://www.w3schools.com"
elementFormDefault="qualified">
<xs:element name="specification">
<xs:complexType>
<xs:sequence>
<xs:element name="type" type="xs:string"/>
<xs:element name="model" type="xs:string"/>
<xs:element name="screenSizeInch" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
*为了根据给定模式验证 XML 文档,我们运行 xmllint并使用–schema*选项后跟模式文件路径。**例如:
$ xmllint --noout --schema laptop.xsd laptop.xml
laptop.xml validates
如果验证失败,我们将看到错误消息输出以及退出代码设置为 3,就像 DTD 的验证一样:
$ xmllint --noout --schema laptop.xsd laptop-invalid.xml
laptop-invalid.xml:6: element model: Schemas validity error : Element '{https://www.w3schools.com}model': This element is not expected. Expected is ( {https://www.w3schools.com}type ).
laptop-invalid.xml fails to validate
在上面的示例中, laptop-invalid.xml缺少 type节点,违反了模式 laptop.xsd。因此,该命令会导致我们在标准错误中观察到的错误。
5. 使用 XPath 查询 XML
鉴于 XML 文档的结构化特性,很容易在格式良好的 XML 文档中查询和选择不同的节点。特别是,XPath 是事实上的查询语言,用于选择 XML 文档中的节点、属性或文本。
*要在 XML 文档上应用有效的 XPath,我们可以在传递–xpath选项的同时运行xmllint命令 。*例如,我们可以 使用 XPath 表达式//screenSizeInch提取laptop.xml文档的screenSizeInch节点 :
$ xmllint --xpath //screenSizeInch laptop.xml
<screenSizeInch>15</screenSizeInch>
同样,要从节点 screenSizeInch中提取文本,我们可以使用内置的 XPath 方法text():
$ xmllint --xpath '//screenSizeInch/text()' laptop.xml
15
6. 剖析
xmllint命令行工具还通过*–timing 和–repeat *参数提供基本的计时和分析功能。
要获取命令执行所需时间的时间概况,我们可以使用–timing选项运行xmllint:**
$ xmllint --noout --timing laptop.xml
Parsing took 0 ms
Freeing took 0 ms
正如我们所见,时序曲线的最小分辨率是以毫秒为单位的。由于我们的laptop.xml文档很小,因此解析时间不到一毫秒也就不足为奇了。
但是,声称文档的解析时间是 0 毫秒肯定是误导。我们可以解决这个限制的一种方法是简单地重复解析几次,然后得到一个平均值。
xmllint命令提供了–repeat选项,如果通过该选项,将导致命令重复 100 次。**在我们的例子中,这正是我们想要的:
$ xmllint --noout --timing --repeat laptop.xml
100 iterations took 1 ms
从结果中可以看出,解析 laptop.xml 100 次需要1 毫秒。从那里,我们可以简单地除以 100 来了解每次迭代大约需要 10 微秒。
7. xmllint交互模式
xmllint带有一个 shell 模式。它允许我们使用几个命令来导航和探索给定的 XML 文档。这在我们浏览大型 XML 文档时特别有用。
此外,当我们继续进行命令演示时,我们会看到这些命令与 Linux 用户每天用于在文件系统之间导航的命令非常相似。例如,要查看节点的内容,我们使用cat。要跳转到不同的节点,我们使用cd等。
要在 XML 文档上启动 shell 会话,我们运行 xmllint –shell后跟 XML 文档名称:
$ xmllint --shell laptop.xml
/ >
运行命令后,我们将进入 shell 提示符,并从正斜杠指示的 XML 文档的根节点开始。
7.1.导航 XML
**与 Linux 的 cd类似,xmllint交互模式下的 cd命令 允许我们“进入”不同的节点。**例如,从根节点,我们可以 cd进入 specification节点:
/ > cd specification
specification >
请注意,一旦我们进入不同的节点,命令提示符将相应地更新当前节点的值。在我们的终端中,我们看到当前节点指示器更改为specification。
**接下来,我们可以使用pwd快速检查当前的 XML 节点路径。**例如, 当我们在 notebook.xml 的 type节点中运行pwd会准确地告诉我们该节点的路径:
type > pwd
/specification/type
7.2. 打印 XML 节点
**在 shell 中,我们可以使用cat命令查看节点的内容。**此外,此命令将可选节点名称作为参数来显示内容。如果未指定,它将显示当前节点的内容。例如,当仍在根节点上时,运行cat只是显示整个文档的内容。
/ > cat
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE specification SYSTEM "laptop.dtd">
<specification>
<type>laptop</type>
<model>macbook</model>
<screenSizeInch>15</screenSizeInch>
</specification>
如果我们指定 model的路径,我们将只获得该节点的输出:
/ > cat /specification/model
-------
<model>macbook</model>
7.3. 将当前节点写入文件
**在 XML 节点上,我们可以使用 后跟文件名的write 命令将当前节点保存到该文件中。**例如,我们可以先 cd进入 model节点:
/ > cd /specification/model
model >
然后,我们可以 使用write命令将model节点保存到 laptop-model.xml中:
model > write laptop-model.xml
检查 laptop-model.xml的内容会发现它只包含model节点:
$ cat laptop-model.xml
<model>macbook</model>
7.4. 退出shell
最后,要退出 shell,我们可以使用exit、quit、bye命令或 CTRL + C。