我有一个xml文件,如下所示
<Module dataPath="/abc/def/xyz" handler="DataRegistry" id="id1" path="test.so"/>
<Module id="id2" path="/my/file/path">
<Config>
<Source cutoffpackage="1" dailyStart="20060819" dataPath="/abc/def/xyz" />
<Source cutoffpackage="1" dailyStart="20060819" dataPath="/abc/def/xyz" id="V2"/>
</Config>
</Module>
我只想从每个dataPath
中提取moduleid
的值。
我正在使用
之类的命令`grep 'id2' file | grep -ioPm1 "(?<=DataPath=)[^ ]+"`
这是从第一个模块ID给我的,而不是从第二个模块ID给我的。因为第二个模块在多行中。
如何使用Shell脚本执行此操作?
所需的输出将是–如果我想获取id1模块的数据路径,则应该获取
/my/file/path
对于第二个模块id,例如id2,我应该用逗号分隔数据路径
/my/file/path, /my/file/path
或者,我第二种grep数据路径的方法是只在newline character
和<Module
之间替换</Module>
,那么我可以使用grep。
答案 0 :(得分:2)
-m1
告诉grep在第一行匹配行后退出,这就是为什么它仅输出一行输出的原因。
我不会为此使用面向行的工具。有更方便的工具来解析XML,例如xmlstarlet:
xml sel -t -m '//@dataPath' -v . -n file.xml
答案 1 :(得分:1)
首先,我的答案假设您具有实际格式正确的源XML。您提供的示例代码没有根元素-但我仍然假设存在一个根元素。
Bash功能本身并不十分适合解析XML。
此著名的Bash FAQ指出:
如果必须使用Shell脚本,则使用XML特定的命令行工具,例如undesired results或XMLStarlet。如果尚未安装XML Starlet,请参阅下载信息xsltproc。
给出您的源XML和所需的输出,请考虑使用以下here模板来实现此目的。
template.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text"/>
<xsl:template match="node()|@*">
<xsl:apply-templates select="node()|@*"/>
</xsl:template>
<xsl:template match="Module">
<xsl:choose>
<xsl:when test="@dataPath and not(descendant::*/@dataPath)">
<xsl:value-of select="@dataPath"/>
<xsl:text>
</xsl:text>
</xsl:when>
<xsl:when test="not(@dataPath) and descendant::*/@dataPath">
<xsl:for-each select="descendant::*/@dataPath">
<xsl:value-of select="."/>
<xsl:if test="position()!=last()">
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:when>
<xsl:when test="@dataPath and descendant::*/@dataPath">
<xsl:value-of select="@dataPath"/>
<xsl:text>, </xsl:text>
<xsl:for-each select="descendant::*/@dataPath">
<xsl:value-of select="."/>
<xsl:if test="position()!=last()">
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
然后运行;
以下XML Starlet命令:
$ xml tr /path/to/template.xsl /path/to/input.xml
或以下xsltproc
命令:
$ xsltproc /path/to/template.xsl /path/to/input.xml
注意:应该将上述命令中template.xsl
和input.xml
的路径名重新定义为这些文件所在的位置。 < / p>
上面的两个命令本质上都会转换您的input.xml
文件并输出所需的结果。
使用以下input.xml
文件:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<Module dataPath="/abc/def/1" handler="DataRegistry" id="id1" path="test.so"/>
<Module id="id2" path="/my/file/path">
<Config>
<Source cutoffpackage="1" dailyStart="20060819" dataPath="/abc/def/2" />
<Source cutoffpackage="1" dailyStart="20060819" dataPath="/abc/def/3" id="V2"/>
</Config>
</Module>
<Module id="id3" path="/my/file/path" dataPath="/abc/def/4">
<Config>
<Source cutoffpackage="1" dailyStart="20060819" dataPath="/abc/def/5" />
<Source cutoffpackage="1" dailyStart="20060819" dataPath="/abc/def/6" id="V2"/>
</Config>
</Module>
<Module id="id4" path="/my/file/path" dataPath="/abc/def/7"/>
<Module id="id5" path="/my/file/path" dataPath="/abc/def/8"/>
<!-- The following <Module>'s have no associated `dataPath` attribute -->
<Module id="id6">
<Config>
<Source cutoffpackage="1" dailyStart="20060819" id="V2"/>
</Config>
</Module>
<Module id="id7"/>
</root>
然后运行上述任何一个命令,都会显示以下结果:
/abc/def/1
/abc/def/2, /abc/def/3
/abc/def/4, /abc/def/5, /abc/def/6
/abc/def/7
/abc/def/8
如果您想避免使用单独的.xsl
文件,则可以按以下步骤在shell脚本中内联上述XSLT模板:
script.sh
#!/usr/bin/env bash
xslt() {
cat <<EOX
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text"/>
<xsl:template match="node()|@*">
<xsl:apply-templates select="node()|@*"/>
</xsl:template>
<xsl:template match="Module">
<xsl:choose>
<xsl:when test="@dataPath and not(descendant::*/@dataPath)">
<xsl:value-of select="@dataPath"/>
<xsl:text>
</xsl:text>
</xsl:when>
<xsl:when test="not(@dataPath) and descendant::*/@dataPath">
<xsl:for-each select="descendant::*/@dataPath">
<xsl:value-of select="."/>
<xsl:if test="position()!=last()">
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:when>
<xsl:when test="@dataPath and descendant::*/@dataPath">
<xsl:value-of select="@dataPath"/>
<xsl:text>, </xsl:text>
<xsl:for-each select="descendant::*/@dataPath">
<xsl:value-of select="."/>
<xsl:if test="position()!=last()">
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
EOX
}
# 1. Using XML Startlet
xml tr <(xslt) /path/to/input.xml
# 2. Or using xsltproc
xsltproc <(xslt) - </path/to/input.xml
注意:您的input.xml
的路径名(即上面/path/to/input.xml
中的script.sh
部分)应重新定义为文件驻留。