模式匹配后如何删除匹配块
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。
这是文件(命名为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。