如何在 Bash 脚本中提取 XML 标记值的一部分

如何在 Bash 脚本中提取 XML 标记值的一部分

我有一个像这样的 XML 文件 ( A.xml):

<?xml version="1.0"?>
<RunParameters xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <RunParametersVersion>NextSeq_4_0_0</RunParametersVersion>
  <ReagentKitSerialWasEnteredInBaseSpace>false</ReagentKitSerialWasEnteredInBaseSpace>
  <ExperimentName>210913-RUN61-COCO</ExperimentName>
  <PurgeConsumables>false</PurgeConsumables>
  <MaxCyclesSupportedByReagentKit>92</MaxCyclesSupportedByReagentKit>
  <ModuleName />
  <ModuleVersion />
</RunParameters>

我想设置一个包含RUN61XML 标记端口的bash 变量<ExperimentName>210913-RUN61-COCO</ExperimentName>。标签值始终具有以下结构

无关紧要的-相关的-不相关

用破折号分隔。

我尝试过grep但没有任何好的结果:

runNumber=$(grep -o '<ExperimentName>.*</ExperimentName>' | cut -d '-' -f2 A.xml)

你知道该怎么做吗?

答案1

由于您正在处理结构化数据,因此您应该使用专用解析器,例如xmlstarlet提取标签值以馈送到cut

xmlstarlet sel -t -c "string(/RunParameters/ExperimentName)" A.xml | cut -d- -f 2

所以,你可以使用

runNumber=$(xmlstarlet sel -t -c "string(/RunParameters/ExperimentName)" A.xml | cut -d- -f 2)

答案2

仅使用xmlstarlet

experiment_name=$(
    xmlstarlet sel -t \
        -m '/RunParameters/ExperimentName' \
        -v 'substring-before(substring-after(., "-"), "-")' file.xml
)

这与我们感兴趣的节点相匹配,然后使用两个函数substring-after()和去掉该节点值的中间部分subsring-before()

然后将 的输出xmlstarlet分配给变量experiment_name

或者,使用xqfromhttps://kislyuk.github.io/yq/

experiment_name=$(
    xq -r '.RunParameters.ExperimentName | split("-")[1]' file.xml
)

这只是在破折号上分割节点的值并返回结果数组中的第二个元素。

答案3

使用 Raku(以前称为 Perl_6)

raku -MXML -e 'for open-xml($*ARGFILES) {.elements(:TAG<ExperimentName>)>>.contents.put};' < input.xml

输入示例:

<?xml version="1.0"?>
<RunParameters xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <RunParametersVersion>NextSeq_4_0_0</RunParametersVersion>
  <ReagentKitSerialWasEnteredInBaseSpace>false</ReagentKitSerialWasEnteredInBaseSpace>
  <ExperimentName>210913-RUN61-COCO</ExperimentName>
  <PurgeConsumables>false</PurgeConsumables>
  <MaxCyclesSupportedByReagentKit>92</MaxCyclesSupportedByReagentKit>
  <ModuleName />
  <ModuleVersion />
</RunParameters>

示例输出:

210913-RUN61-COCO

正如其他人所提到的,您肯定会希望使用专用的 XML 解析器来完成此任务。简而言之,对于上述代码,Raku 在 bash 命令行中调用,并使用命令 加载-M模块。请注意,上面的代码依赖于shell 重定向[没有重定向,您必须将输入字符串化]。 xml 文件使用 打开并查询所需内容,使用 进行提取和返回。XML-MXML<open-xml()$*ARGFILES.Stropen-xmlTAGcontentsput

实际上,OP 提供了非常好的代码,用于cut提取RUN61输出部分,并且上述 Raku 解决方案可以简单地通过 OP 的代码进行管道传输。然而,对于全 Raku 解决方案,只需在上面的 Raku 代码中插入.split("-")[1]对 和.contains的调用:.put

raku -MXML -e 'for open-xml($*ARGFILES.Str) {.elements(:TAG<ExperimentName>)>>.contents.split("-")[1].put};'

https://github.com/raku-community-modules/XML
https://www.raku.org

答案4

您还可以通过grep(选项-E允许扩展正则表达式)提取该名称:

runNumber=$(grep -Eo '[[:alnum:]]+-[[:alnum:]]+' A.xml | cut -d- -f2)

如果你想确保该标签的行,你可以通过另一个grep命令对其进行预过滤:

runNumber=$(
  grep '<ExperimentName>' A.xml \
  | grep -Eo '[[:alnum:]]+-[[:alnum:]]+' \
  | cut -d- -f2
)

笔记:

基于 XPath 表达式的解决方案:

  • 更具可读性
  • 可能具有更高的容错能力
  • 他们可以引入一些额外的依赖项

相关内容