【问题标题】:AWK / SED extract string between HUGE lineAWK / SED在HUGE行之间提取字符串
【发布时间】:2016-04-26 20:33:57
【问题描述】:

我有一条来自 ws 的响应的大行,我需要获取 <asunto></asunto> 之间的所有字符串。文件是这样的:

Content-Type: application/xop+xml; charset=UTF-8; type="application/soap+xml";
Content-Transfer-Encoding: binary
Content-ID: <root.message@cxf.apache.org>

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"><soap:Body><ns1:consultarComunicacionesResponse xmlns:ns1="http://ve.tecno.afip.gov.ar/domain/service/ws"><ns2:RespuestaPaginada xmlns:ns2="http://ve.tecno.afip.gov.ar/domain/service/ws" xmlns:ns3="http://core.tecno.afip.gov.ar/model/ws/types" xmlns:ns4="http://ve.tecno.afip.gov.ar/domain/service/ws/types"><pagina>1</pagina><totalPaginas>1</totalPaginas><itemsPorPagina>100</itemsPorPagina><totalItems>2</totalItems><ns4:items><ns4:ComunicacionSimplificada><idComunicacion>sdfgsfdgsfdgsd</idComunicacion><cuitDestinatario>sdfgsdfgsdfgsfdg</cuitDestinatario><fechaPublicacion>sdfgsdfg</fechaPublicacion><fechaVencimiento>sdfgsdfgsdfg</fechaVencimiento><sistemaPublicador>sdfgsdfgsfg</sistemaPublicador><sistemaPublicadorDesc>sdfgsdfggf</sistemaPublicadorDesc><estado>2</estado><estadoDesc>sdfgsdfgsgf</estadoDesc><asunto>EXAMPLEEEEEEEEEEEEEEEE1</asunto><prioridad>3</prioridad><tieneAdjunto>sdfgfdg</tieneAdjunto></ns4:ComunicacionSimplificada><ns4:ComunicacionSimplificada><idComunicacion>sdfgsdfgdfg</idComunicacion><cuitDestinatario>sdfgdfsg</cuitDestinatario><fechaPublicacion>sdfgsdfg</fechaPublicacion><fechaVencimiento>sdfgdsfg</fechaVencimiento><sistemaPublicador>sdfgsdfg</sistemaPublicador><sistemaPublicadorDesc>sdfgsdfgdsfggsdf</sistemaPublicadorDesc><estado>1</estado><estadoDesc>dsfgsdfgsgd</estadoDesc><asunto>EXAMPLEEEEEEEEEEEEEEEE2</asunto><prioridad>asdfdsf</prioridad><tieneAdjunto>asdfasdf</tieneAdjunto></ns4:ComunicacionSimplificada></ns4:items></ns2:RespuestaPaginada></ns1:consultarComunicacionesResponse></soap:Body></soap:Envelope>    

我应该得到这样的东西:

EXAMPLEEEEEEEEEEEEEEEE1    
EXAMPLEEEEEEEEEEEEEEEE2

可能有很多重复,在 0 到数百之间。

谢谢!!

【问题讨论】:

    标签: string awk sed line delimiter


    【解决方案1】:

    awk 来救援!

    $ awk -v RS='[<>]' '/\/asunto/{f=0;next} f; /asunto/{f=1}' file
    
    EXAMPLEEEEEEEEEEEEEEEE1
    EXAMPLEEEEEEEEEEEEEEEE2
    

    更新:根据 cmets,如果标签有可能存在于其他地方,您可以锚定在打开/关闭标签的左侧和右侧

    $ awk -v RS='[<>]' '/^\/asunto$/{f=0;next} f; /^asunto$/{f=1}' file
    EXAMPLEEEEEEEEEEEEEEEE1
    EXAMPLEEEEEEEEEEEEEEEE2
    

    或等效地,检查字符串是否完全匹配

    $ awk -v RS='[<>]' '$0=="/asunto"{f=0;next} f; $0=="asunto"{f=1}' file
    EXAMPLEEEEEEEEEEEEEEEE1
    EXAMPLEEEEEEEEEEEEEEEE2
    

    另请注意,并非所有 awk 变体都支持多字符 RS。

    【讨论】:

    • 你应该提到它是 gawk 特定的,因为它是多字符 RS,如果 asunto 出现在其他上下文中而不是作为标签,它将失败。
    • @karakfa - 为了解决 Ed 的第二点,您能否将单行稍微更改为:awk -v RS='[&lt;&gt;]' '/\/asunto *$/{f=0;next} f; /^asunto/{f=1}' file
    【解决方案2】:

    您也可以使用 GNU grep

    grep -oP '(?<=<asunto>)((?!</asunto>).)+(?=</asunto>)' yourfile
    

    这利用了 Lookbehind 加上 NegativePositive Lookahead

    Here's 很好地解释了它的内部结构。

    性能

    $ wc -l bigfile 
    100000 bigfile
    
    $ time awk -v RS='</?asunto>' '!(NR%2)' bigfile >/dev/null
    
    real  0m0.277s
    user  0m0.254s
    sys 0m0.022s
    
    
    $ time grep -oP '(?<=<asunto>)((?!</asunto>).)+(?=</asunto>)' bigfile >/dev/null
    
    real  0m4.318s
    user  0m4.292s
    sys 0m0.020s
    
    $ time awk -v RS='[<>]' '/\/asunto/{f=0;next} f; /asunto/{f=1}' bigfile >/dev/null
    
    real  0m7.088s
    user  0m6.928s
    sys 0m0.021s
    

    @Ed 代码实现了迄今为止最好的性能。

    【讨论】:

      【解决方案3】:

      使用 GNU awk 进行多字符 RS:

      $ awk -v RS='</?asunto>' '!(NR%2)' file
      EXAMPLEEEEEEEEEEEEEEEE1
      EXAMPLEEEEEEEEEEEEEEEE2
      

      【讨论】:

        【解决方案4】:

        使用 XML 解析器(和 awk 删除标头)

        awk -v RS= 'NR>1' ws.out | xmlstarlet sel  -t -v //asunto -n
        

        【讨论】:

          【解决方案5】:

          这可能对你有用(GNU sed):

          sed -nr '/<asunto>([^<]*)<\/asunto>/{s//\n\1\n/;s/[^\n]*\n//;P;D}' file
          

          这会将字符串缩减为前置行,然后打印、删除该行并重复。不包含所需字符串的行将被忽略。

          【讨论】:

            【解决方案6】:

            正如在其他地方指出的那样,XML 感知工具原则上会更安全,但如果没有嵌套“asunto”标签,以下 GNU grep 咒语可能会很有用,并且即使 @987654321 之间的字符串也可以工作@ 和&lt;/asunto&gt; 为空或包含其他标签:

            grep -oP '(?<=<asunto>).*?(?=</asunto>)'
            

            这里的关键是非贪心子表达式:.*?

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-09-16
              • 2012-08-18
              相关资源
              最近更新 更多