XML 命令行(shell 脚本)操作

XML 命令行(shell 脚本)操作

如何在 shell 脚本中从命令行操作 XML?

有很多用于操作表格数据、替换环境变量或用正则表达式替换文本片段的命令,但我还没有找到任何用于 XML 的命令。

我的构建脚本需要在 xml 文档的主标签中插入一个包含内容的标签,并且我发现为此目的在操作系统中安装 java、perl 或 python 是一种矫枉过正的做法(我的脚本是在 gitlab 中使用 docker 镜像完成的,所以这样做我使用 maven:3.5-jdk-8 映像中可用的工具进行的工作将是一个梦想)。

我不想用 sed 操作 XML,尽管在我的构建脚本中它可以工作,因为它是邪恶的

示例:我有以下 xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>  
  <!-- a lot of other tags-->
</project>  

我想插入以下块:

<distributionManagement>
    <repository>
        <id>private-releases</id>
        <url>https://my.private.server.com/nexus/repository/maven-releases/</url>
    </repository>
</distributionManagement>

在项目标签内(并且它是在开始还是在结束完全无关紧要。

答案1

XMLStarlet (http://xmlstar.sourceforge.net/overview.php) 用 C 编写并使用libxml2libxslt

给定 XML 文档

<?xml version="1.0"?>
<root>
  <tag>data</tag>
</root>

root可以使用插入子节点

xml ed -s '/root' -t elem -n 'newtag' -v 'newdata' file.xml

产生

<?xml version="1.0"?>
<root>
  <tag>data</tag>
  <newtag>newdata</newtag>
</root>

file.xml插入很多东西(这里使用顶部的原始内容):

xml ed -s '/root' -t elem -n 'newtag' \
       -s '/root/newtag' -t elem -n 'subtag' -v 'subdata' file.xml

这会产生

<?xml version="1.0"?>
<root>
  <tag>data</tag>
  <newtag>
    <subtag>subdata</subtag>
  </newtag>
</root>

对于问题中的例子:

xml ed -N x="http://maven.apache.org/POM/4.0.0" \
       -s '/x:project' -t elem -n 'distributionManagement' \
       -s '/x:project/distributionManagement' -t elem -n 'repository' \
       -s '/x:project/distributionManagement/repository' -t elem -n 'id' \
         -v 'private-releases' \
       -s '/x:project/distributionManagement/repository' -t elem -n 'url' \
         -v 'https://my.private.server.com/nexus/repository/maven-releases/' \
    file.xml

结果:

<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <!-- a lot of other tags-->
  <distributionManagement>
    <repository>
      <id>private-releases</id>
      <url>https://my.private.server.com/nexus/repository/maven-releases/</url>
    </repository>
  </distributionManagement>
</project>

将先前准备好的 XML 文件插入到 XML 中的某个位置:

假设问题中的原始 XML 位于file.xml并且新节点中应包含的附加位distributinManagement位于new.xml(但是不是节点标签本身),一可以执行以下操作插入new.xml根节点:

xml ed -N x="http://maven.apache.org/POM/4.0.0" \
       -s '/x:project' -t elem -n 'distributionManagement' \
       -v "$(<new.xml)" file.xml | xml unesc | xml fo

XMLStarlet会自动转义需要转义的数据,例如<>字符。那个xml unesc逃逸插入的数据(它实际上对整个文档进行转义,这可能是也可能不是问题),并xml fo重新格式化生成的 XML 文档。

结果是

<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <!-- a lot of other tags-->
  <distributionManagement>
    <repository>
      <id>private-releases</id>
      <url>https://my.private.server.com/nexus/repository/maven-releases/</url>
    </repository>
  </distributionManagement>
</project>

我对这样做有点不安,“但它有效”。

另请参阅 StackOverflow 上的相关问题:https://stackoverflow.com/questions/29298507/xmlstarlet-xinclude-xslt

答案2

我发现为此目的在操作系统中安装 java、perl 或 python 太过分了(我的脚本是在 gitlab 中使用 docker 映像完成的,因此使用 maven:3.5-jdk-8 映像中提供的工具完成我的工作将是一个梦想)。

它可能仍然是矫枉过正,但如果您只关心容器的大小,您可以使用非常轻量级的语言,例如 Lua 或 Guile。

来自 Lua 文档:

将 Lua 添加到应用程序不会使其变得臃肿。 Lua 5.3.4 的 tarball 包含源代码和文档,压缩后为 297K,未压缩为 1.1M。源代码包含大约 24000 行 C 代码。在 64 位 Linux 下,使用所有标准 Lua 库构建的 Lua 解释器需要 246K,Lua 库需要 421K。

相关内容