删除XML文件中与条件不匹配的部分-使用bash

删除XML文件中与条件不匹配的部分-使用bash,xml,bash,awk,Xml,Bash,Awk,我有一个大的XML文件,看起来像这样: <A> <B id="XXX_City_Oslo"> <C> .... </C> </B> <B id="XXX_City_Bergen"> <C> .... </C> </B>

我有一个大的XML文件,看起来像这样:

<A>
    <B id="XXX_City_Oslo">
        <C>
        ....
        </C>
    </B>
    <B id="XXX_City_Bergen">
        <C>
        ....
        </C>
    </B>
    <B id="XXX_City_Trondheim">
        <C>
        ....
        </C>
    </B>
    <B id="XXX_City_Stavanger">
        <C>
        ....
        </C>
    </B>
    <B id="1">
        <C>
        ....
        </C>
    </B>
    <B id="2">
        <C>
        ....
        </C>
    </B>

</A>

....
....
....
....
....
....
我希望删除一些包含字符串“City”的章节及其内容。XML文件将定义所有应该删除的部分。所以更容易定义应该保留哪些城市。唯一的问题是所有像“1”和“2”这样的部分,我也想保留这些部分不包含字符串“City”

假设我想保留奥斯陆和斯塔万格,使用以下命令:

awk'/&&/id=“XXX_城市_奥斯陆”/&/id=“XXX_City_Stavanger”//{next}1'
这将删除所有的B部分,但离开奥斯陆和斯塔万格。这里的问题是,这还将删除不包含字符串“City”的其他B部分。 只删除与给定输入不匹配的城市,而不删除所有不包含字符串“City”的部分,这是一种简单的方法吗?类似这样的内容(请注意/id=“City”/):

awk'/&***/id=“*城市*”/***&&/id=“XXX_城市_奥斯陆”/&/id=“XXX_City_Stavanger”//{next}1'

请注意,这是在linux环境下运行的,没有太多添加其他脚本语言/库的选项,我希望使用awk采用相同的方法来解决这个问题


提前感谢您的任何贡献

对于
AWK
/../
是一个regexp模式匹配表达式

因此,您只需在其他过滤器上添加一个
City
过滤器:

awk'/&&/id=“XXX_城市_奥斯陆”/&/id=“XXX_City_Stavanger”/&&/City/,/{next}1”
编辑:正如@EdMorton在评论中所建议的,您可以将其缩减为:

awk'/&&/id=“XXX_城市(奥斯陆|斯塔万格)”/,/{next}1'

如果您打算在生产脚本中使用它,正如@EdMorton所说,您应该避免硬编码标记标识符。

使用
AWK
/../
是一个regexp模式匹配表达式

$ cat tst.awk
BEGIN {
    split(tgts,tmp,/,/)
    for (i in tmp) {
        goodCities["XXX_City_"tmp[i]]
    }
    FS = "\""
    inGoodBlock = 1
}
/^[[:space:]]*<B[[:space:]]*id="/ {
    inGoodBlock = ( ($2 ~ /_City_/) && !($2 in goodCities) ? 0 : 1 )
}
inGoodBlock
/^[[:space:]]*<\/B>/ {
    inGoodBlock = 1
}
因此,您只需在其他过滤器上添加一个
City
过滤器:

awk'/&&/id=“XXX_城市_奥斯陆”/&/id=“XXX_City_Stavanger”/&&/City/,/{next}1”
编辑:正如@EdMorton在评论中所建议的,您可以将其缩减为:

awk'/&&/id=“XXX_城市(奥斯陆|斯塔万格)”/,/{next}1'
如果您打算在生产脚本中使用它,正如@EdMorton所说,您应该避免硬编码您的标记标识符。

$cat tst.awk
$ cat tst.awk
BEGIN {
    split(tgts,tmp,/,/)
    for (i in tmp) {
        goodCities["XXX_City_"tmp[i]]
    }
    FS = "\""
    inGoodBlock = 1
}
/^[[:space:]]*<B[[:space:]]*id="/ {
    inGoodBlock = ( ($2 ~ /_City_/) && !($2 in goodCities) ? 0 : 1 )
}
inGoodBlock
/^[[:space:]]*<\/B>/ {
    inGoodBlock = 1
}
开始{ 拆分(tgts、tmp、/、/) 对于(tmp中的i){ 好城市[“XXX_城市”tmp[i]] } FS=“\”“ inGoodBlock=1 } /^[[:space:][]*
$cat tst.awk
开始{
拆分(tgts、tmp、/、/)
对于(tmp中的i){
好城市[“XXX_城市”tmp[i]]
}
FS=“\”“
inGoodBlock=1
}

/^[[:space:][]*尽管您提到无法安装新库,但我仍然建议使用支持XML的工具处理XML。例如,在XML中,您只需编写
rm/A/B[contains(@id,“City”)而不是(xsh:match(@id,“Bergen | Stavanger”)]
。Awk是用于此作业的错误工具。这个网站充斥着关于如何处理坏XML的查询,而这些坏XML都是由那些选择使用错误的工具来处理XML的人创建的。@MichaelKay这在理论上是好的,但没有强制性的POSIX工具来解析XML,而且通常要解析的XML是一个非常特定的、受限的XML子集,通常由其他工具以非常特定的布局生成,因此在某些情况下,awk是完全足够的和/或唯一可用于作业的工具。GNU awk有一个XML库,用户可以顺便安装,所以你甚至不能说“awk是一个错误的工作工具”,因为这听起来好像没有一个awk有XML解析器。@MichaelKay我同意这不是讨论的地方,但我很好奇我让你不同意的陈述是:a)没有强制性的POSIX工具来解析XML,或者b)通常要解析的XML是一个非常特定的、受限的XML子集,通常由工具生成;或者c)在这种情况下,awk是完全足够的和/或唯一可用于该作业的工具;或者d)GNU awk有一个XML库,用户可以安装。我还想知道,如果您在POSIX系统上,无法安装非强制性工具,并且必须处理包含受限XML集的文件,您会怎么做(a)正确的工具不在首选工具箱中,这一事实不能成为使用错误工具的借口。(b) 通过编写只接受XML子集的代码,您对该XML的创建者施加了不合理的依赖:例如,如果他们升级了用于生成XML的某些软件,他们可能会无意中偏离该子集;(c) awk是不够的,因为任何使用正则表达式处理XML的尝试都会遇到安全问题,例如通过利用注释和CDATA节。尽管您注意到无法安装新库,但我仍然建议使用支持XML的工具来处理XML。例如,在XML中,您只需编写
rm/A/B[contains(@id,“City”)而不是(xsh:match(@id,“Bergen | Stavanger”)]
。Awk是用于此作业的错误工具。这个网站充斥着关于如何处理坏XML的查询,而这些坏XML都是由那些选择使用错误的工具来处理XML的人创建的。@MichaelKay这在理论上是好的,但没有强制性的POSIX工具来解析XML,而且通常要解析的XML是一个非常特定的、受限的XML子集,通常由其他工具以非常特定的布局生成,因此在某些情况下,awk是完全足够的和/或唯一可用的工具
$ awk -v tgts='Trondheim' -f tst.awk file
<A>
    <B id="XXX_City_Trondheim">
        <C>
        ....
        </C>
    </B>
    <B id="1">
        <C>
        ....
        </C>
    </B>
    <B id="2">
        <C>
        ....
        </C>
    </B>

</A>