AWK/SED 提取大行之间的字符串
AWK / SED extract string between HUGE line
我有一条巨大的线是来自 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到百之间。
谢谢!!
awk
救援!
$ awk -v RS='[<>]' '/\/asunto/{f=0;next} f; /asunto/{f=1}' file
EXAMPLEEEEEEEEEEEEEEEE1
EXAMPLEEEEEEEEEEEEEEEE2
更新:根据评论,如果标签有可能存在于其他地方,您可以锚定在 open/close 标签的左侧和右侧
$ awk -v RS='[<>]' '/^\/asunto$/{f=0;next} f; /^asunto$/{f=1}' file
EXAMPLEEEEEEEEEEEEEEEE1
EXAMPLEEEEEEEEEEEEEEEE2
或等效地,检查字符串是否完全匹配
$ awk -v RS='[<>]' '[=12=]=="/asunto"{f=0;next} f; [=12=]=="asunto"{f=1}' file
EXAMPLEEEEEEEEEEEEEEEE1
EXAMPLEEEEEEEEEEEEEEEE2
另请注意,并非所有 awk
变体都支持多字符 RS。
你也可以使用 GNU grep
.
grep -oP '(?<=<asunto>)((?!</asunto>).)+(?=</asunto>)' yourfile
这利用了 Lookbehind 加上 Negative 和 Positive 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 代码实现了迄今为止最好的性能。
使用 GNU awk for multi-char RS:
$ awk -v RS='</?asunto>' '!(NR%2)' file
EXAMPLEEEEEEEEEEEEEEEE1
EXAMPLEEEEEEEEEEEEEEEE2
使用 XML 解析器(并使用 awk 删除 header)
awk -v RS= 'NR>1' ws.out | xmlstarlet sel -t -v //asunto -n
这可能对你有用 (GNU sed):
sed -nr '/<asunto>([^<]*)<\/asunto>/{s//\n\n/;s/[^\n]*\n//;P;D}' file
这会将字符串缩减为前置行,然后打印、删除该行并重复。不包含所需字符串的行将被忽略。
正如别处指出的那样,XML-aware 工具原则上会更安全,但如果没有 "asunto" 标记的嵌套,以下 GNU grep 咒语可能会有用,甚至可以工作如果 <asunto>
和 </asunto>
之间的字符串为空或包含其他标签:
grep -oP '(?<=<asunto>).*?(?=</asunto>)'
这里的关键是 non-greedy 子表达式:.*?
我有一条巨大的线是来自 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到百之间。
谢谢!!
awk
救援!
$ awk -v RS='[<>]' '/\/asunto/{f=0;next} f; /asunto/{f=1}' file
EXAMPLEEEEEEEEEEEEEEEE1
EXAMPLEEEEEEEEEEEEEEEE2
更新:根据评论,如果标签有可能存在于其他地方,您可以锚定在 open/close 标签的左侧和右侧
$ awk -v RS='[<>]' '/^\/asunto$/{f=0;next} f; /^asunto$/{f=1}' file
EXAMPLEEEEEEEEEEEEEEEE1
EXAMPLEEEEEEEEEEEEEEEE2
或等效地,检查字符串是否完全匹配
$ awk -v RS='[<>]' '[=12=]=="/asunto"{f=0;next} f; [=12=]=="asunto"{f=1}' file
EXAMPLEEEEEEEEEEEEEEEE1
EXAMPLEEEEEEEEEEEEEEEE2
另请注意,并非所有 awk
变体都支持多字符 RS。
你也可以使用 GNU grep
.
grep -oP '(?<=<asunto>)((?!</asunto>).)+(?=</asunto>)' yourfile
这利用了 Lookbehind 加上 Negative 和 Positive 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 代码实现了迄今为止最好的性能。
使用 GNU awk for multi-char RS:
$ awk -v RS='</?asunto>' '!(NR%2)' file
EXAMPLEEEEEEEEEEEEEEEE1
EXAMPLEEEEEEEEEEEEEEEE2
使用 XML 解析器(并使用 awk 删除 header)
awk -v RS= 'NR>1' ws.out | xmlstarlet sel -t -v //asunto -n
这可能对你有用 (GNU sed):
sed -nr '/<asunto>([^<]*)<\/asunto>/{s//\n\n/;s/[^\n]*\n//;P;D}' file
这会将字符串缩减为前置行,然后打印、删除该行并重复。不包含所需字符串的行将被忽略。
正如别处指出的那样,XML-aware 工具原则上会更安全,但如果没有 "asunto" 标记的嵌套,以下 GNU grep 咒语可能会有用,甚至可以工作如果 <asunto>
和 </asunto>
之间的字符串为空或包含其他标签:
grep -oP '(?<=<asunto>).*?(?=</asunto>)'
这里的关键是 non-greedy 子表达式:.*?