模式匹配后如何删除匹配块

How to delete a matching block once a pattern is matched

这是文件(命名为sample.xml):


<?xml version="1.0" encoding="UTF-8"?>
<configs>

    <blah1 value="ma">
      <tag3>100MB</tag3>
    </blah1>

    <blah1 value="ba">
      <tag3>20MB</tag3>
    </blah1>

     <blah2 value="*" version="1.0" result="true">
        <blah1 value="xyz">
          <blah1 value="uvw" result="true">
             <tag>4</tag>
          </blah1>
        </blah1>
     </blah2>

  <!-- This is tag with def value -->
  <blah2 value="*" version="2.0" result="true">
    <blah1 value="abc">
      <blah1 value="def" result="true">
        <tag2>on</tag2>
      </blah1>
    </blah1>
  </blah2>

</configs>

找到带有 value="def" 的字符串时,删除从 <blah2> to </blah2> 个标签开始的整个块

我不熟悉 sed 保持模式,但我从 google 那里得到的东西非常接近

sed -n '/<blah2.*>/,/<\/blah2>/{
                                  H
                                  /<\/blah2>/ { 
                                        s/.*//;x
                                       /def/d
                                       p 
                                  }
                               }' sample.xml

预期结果:


<?xml version="1.0" encoding="UTF-8"?>
<configs>

    <blah1 value="ma">
      <tag3>100MB</tag3>
    </blah1>

    <blah1 value="ba">
      <tag3>20MB</tag3>
    </blah1>

     <blah2 value="*" version="1.0" result="true">
        <blah1 value="xyz">
          <blah1 value="uvw" result="true">
             <tag>4</tag>
          </blah1>
        </blah1>
     </blah2>

</configs>

实际结果(上面的非工作 sed):

     <blah2 value="*" version="1.0" result="true">
        <blah1 value="xyz">
          <blah1 value="uvw" result="true">
             <tag>4</tag>
          </blah1>
        </blah1>
     </blah2>

这可能适合您 (GNU sed):

sed '/<blah2.*>/{:a;N;/<\/blah2.*>/!ba;/value="def"/d}' file

如果一行包含 <blah2.*> 收集所有行直到包含 <\/blah2.*> 的行,然后测试字符串 value="def" 的这些行,如果找到,则删除这些行。

用 xmlstarlet 删除第二个标签 blah2:

xmlstarlet edit --delete '//configs[blah2[2]/blah1/blah1[@value="def"]]/blah2[2]' file.xml

输出:

<?xml version="1.0" encoding="UTF-8"?>
<configs>
  <blah1 value="ma">
    <tag3>100MB</tag3>
  </blah1>
  <blah1 value="ba">
    <tag3>20MB</tag3>
  </blah1>
  <blah2 value="*" version="1.0" result="true">
    <blah1 value="xyz">
      <blah1 value="uvw" result="true">
        <tag>4</tag>
      </blah1>
    </blah1>
  </blah2>
</configs>

如果您想就地编辑文件,请添加选项 -L。


使用说明 XPath:

//configs[blah2[2]/blah1/blah1[@value="def"]]/blah2[2]
|---A---| |-------------B------------------| |---C---|

A and B: path to the attribute you are looking for

A and C: path to the tag to be deleted

由于您对 sed 解决方案感到满意,根据您发布的示例,这里有一个更好(更清晰、更便携等)的替代方案 input/output:

$ awk -v RS= -v ORS='\n\n' '!/value="def"/' file
<?xml version="1.0" encoding="UTF-8"?>
<configs>

    <blah1 value="ma">
      <tag3>100MB</tag3>
    </blah1>

    <blah1 value="ba">
      <tag3>20MB</tag3>
    </blah1>

     <blah2 value="*" version="1.0" result="true">
        <blah1 value="xyz">
          <blah1 value="uvw" result="true">
             <tag>4</tag>
          </blah1>
        </blah1>
     </blah2>

</configs>

如果这不是您所需要的全部,那么无论您需要什么,都有一个更好的 awk 替代方案,因为 sed 最适合对单个字符串执行 s/old/new。