如何使用 sed 或 awk 匹配和删除 xml 标签内的第一个和第二个模式?
以下是示例
<data>A78-1-1134-HI-1</data>
<data>T78-12-1346-AG-2</data>
<data>G78-4-2156-Ag-6</data>
<data>A78-10-1971-Hh-10</data>
这是我想要得到的结果:
<data>1134</data>
<data>1346</data>
<data>2156</data>
<data>1971</data
能用一行代码完成吗?这是我尝试的:
sed 's/^.*<data>[[:alnum:]]-[0-9]-/<data>/g;s/-[a-Z].*<\/data>$//g'
或者仅删除第一个模式,当我使用 sed 打印时它就可以工作:
sed -n 's/^.*<data>.*[[:alnum:]]-[0-9]-/<data>/p' file.xml | grep data
但是这个命令将不起作用:
sed 's/^.*<data>.*[[:alnum:]]-[0-9]-/<data>/' file.xml
答案1
以下是一些解决方案:
如果您的文件确实像示例一样简单,您可以使用此脚本来完成
gawk
。这假设您的文件仅包含data
问题中描述的条目。gawk -F"-" '{print "<data>"$3"</data>"}' file.xml
-F"-"
告诉 gawk 将其-
作为字段分隔符,然后脚本打印第 3 个字段。
($1~/data/
对于包含您不想要的行的稍微复杂一些的文件,仅当第一个)和最后一个($NF~/data/
)字段包含以下内容时才会打印data
:gawk -F"-" '($1~/data/ && $NF~/data/){print "<data>"$3"</data>"}' file.xml
如果您的文件可以包含多个
<data>
条目,而您只关心如下条目A1-2B-C3-4D
:perl -ne '/(<data>).+?-.+?-(.+?)\-.+(<\/data>)/ && do{print "$1$2$3\n"}' file.xml
-ne
表示将此脚本应用于输入文件的每一行。在 Perl(以及许多其他工具)中,括号允许捕获正则表达式匹配项。在这里,我捕获了三个模式,即开始和结束标记($1
和$3
),因此我不需要输入两次,以及我们正在寻找的模式$2
。如果您需要更具体,请使用此选项仅允许在第一个字段中使用字母数字字符,而在其他字段中仅允许使用数字:
perl -ne '/(<data>)[\w\d]+?-\d+?-(\d+?)\-.+(<\/data>)/ && do{print "$1$2$3\n"}' file.xml
这一切都假设你的
<data>
和</data>
标签在同一行。如果不是,你可以这样做:perl -ne ' $d++ if /<data>/; /[\w\d]+?-\d+?-(\d+?)\-.+/ && do{ print "<data>$1</data>\n" if $d>0 }; $d-- if /<\/data>/; ' file.xml
$d
如果我们在标签内,则结果为正<data></data>
。如果我们在标签内,并且找到与正则表达式匹配的行,则打印。
更新:
如果您想要编辑文件,而不只是打印其内容而且实际上更改原始文件,请使用以下命令:
perl -i -ne 's/(<data>).+?-.+?-(.+?)\-.+(<\/data>)/$1$2$3/; print' file.xml
答案2
您使用的工具不对。不要用正则表达式解析 XML,否则您会出错。(这是因为 (a) 从理论上讲这是不可能的 - XML 不是正则语言,并且 (b) 您的实际尝试可能适用于某些 XML 文档,但不可避免地会在其他 XML 文档上失败。)
使用 XSLT 2.0,这是一个简单的转换。
<xsl:template match="data">
<xsl:copy>
<xsl:value-of select="tokenize(., '-')[3]"/>
</xsl:copy>
</xsl:template>
答案3
看来您的重复次数没有正确指定。此外,我发现使用子表达式提取子字符串更容易。我不知道您匹配数据的确切规范,但这适用于问题中的样本数据(我思考这是符合 POSIX 标准的):
sed 's/<data>[[:alnum:]]\{1,\}-[0-9]\{1,\}-\([0-9]\{1,\}\)-[[:alnum:]]\{1,\}-[0-9]\{1,\}/<data>\1/' file.xml
如果您可以sed
使用 GNU,您可以利用它的扩展正则表达式扩展来获得更简单的表达式:
sed -r 's/^.*<data>[[:alnum:]]+-[0-9]+-([0-9]+)-[[:alnum:]]+-[0-9]+/<data>\1/' file.xml