我有一个像这样的 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>
我想设置一个包含RUN61
XML 标记端口的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
。
或者,使用xq
fromhttps://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.Str
open-xml
TAG
contents
put
实际上,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 表达式的解决方案:
- 更具可读性
- 可能具有更高的容错能力
- 但他们可以引入一些额外的依赖项