匹配并删除 xml 标签中的第一个和第二个模式

匹配并删除 xml 标签中的第一个和第二个模式

如何使用 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

以下是一些解决方案:

  1. 如果您的文件确实像示例一样简单,您可以使用此脚本来完成gawk。这假设您的文件仅包含data问题中描述的条目。

    gawk -F"-" '{print "<data>"$3"</data>"}' file.xml
    
    • -F"-"告诉 gawk 将其-作为字段分隔符,然后脚本打印第 3 个字段。


  2. ($1~/data/对于包含您不想要的行的稍微复杂一些的文件,仅当第一个)和最后一个($NF~/data/)字段包含以下内容时才会打印data

    gawk -F"-" '($1~/data/ && $NF~/data/){print "<data>"$3"</data>"}' file.xml
    
  3. 如果您的文件可以包含多个<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
    
  4. 这一切都假设你的<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

相关内容