Suponho que o que você postou é uma amostra, porque não é um XML válido. Se essa suposição não for válida, minha resposta não é válida ... mas, se for esse o caso, você realmente precisa atingir a pessoa que forneceu o XML com uma cópia agrupada da especificação XML e exigir consertá-lo '.
Mas realmente - awk
e expressões regulares não são a ferramenta certa para o trabalho. Um analisador XML é. E com um analisador, é absurdamente simples fazer o que você quer:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
#parse your file - this will error if it's invalid.
my $twig = XML::Twig -> new -> parsefile ( 'your_xml' );
#set output format. Optional.
$twig -> set_pretty_print('indented_a');
#iterate all the 'record' nodes off the root.
foreach my $record ( $twig -> get_xpath ( './record' ) ) {
#if - beneath this record - we have a node anywhere (that's what // means)
#with a tag of 'keyword' and content of 'SEARCH'
#print the whole record.
if ( $record -> get_xpath ( './/keyword[string()="SEARCH"]' ) ) {
$record -> print;
}
}
xpath
é bastante parecido com expressões regulares - de certa forma - mas é mais como um caminho de diretório. Isso significa que ele reconhece o contexto e pode manipular estruturas XML.
Acima: ./
significa 'abaixo do nó atual':
$twig -> get_xpath ( './record' )
Significa qualquer tag "top level" <record>
.
Mas .//
significa "em qualquer nível, abaixo do nó atual", por isso o fará recursivamente.
$twig -> get_xpath ( './/search' )
Obteria <search>
nós em qualquer nível.
E os colchetes denotam uma condição - ou é uma função (por exemplo, text()
para obter o texto do nó) ou você pode usar um atributo. por exemplo. //category[@name]
encontraria qualquer categoria com um atributo de nome e //category[@name="xyz"]
filtraria esses atributos.
XML usado para teste:
<XML>
<record category="xyz">
<person ssn="" e-i="E">
<title xsi:nil="true"/>
<position xsi:nil="true"/>
<details>
<names>
<first_name/>
<last_name></last_name>
</names>
<aliases>
<alias>CDP</alias>
</aliases>
<keywords>
<keyword xsi:nil="true"/>
<keyword>SEARCH</keyword>
</keywords>
<external_sources>
<uri>http://www.google.com</uri>
<detail>SEARCH is present in abc for xyz reason</detail>
</external_sources>
</details>
</person>
</record>
<record category="abc">
<person ssn="" e-i="F">
<title xsi:nil="true"/>
<position xsi:nil="true"/>
<details>
<names>
<first_name/>
<last_name></last_name>
</names>
<aliases>
<alias>CDP</alias>
</aliases>
<keywords>
<keyword xsi:nil="true"/>
<keyword>DONTSEARCH</keyword>
</keywords>
<external_sources>
<uri>http://www.google.com</uri>
<detail>SEARCH is not present in abc for xyz reason</detail>
</external_sources>
</details>
</person>
</record>
</XML>
Saída:
<record category="xyz">
<person
e-i="E"
ssn="">
<title xsi:nil="true" />
<position xsi:nil="true" />
<details>
<names>
<first_name/>
<last_name></last_name>
</names>
<aliases>
<alias>CDP</alias>
</aliases>
<keywords>
<keyword xsi:nil="true" />
<keyword>SEARCH</keyword>
</keywords>
<external_sources>
<uri>http://www.google.com</uri>
<detail>SEARCH is present in abc for xyz reason</detail>
</external_sources>
</details>
</person>
</record>
Nota - o acima apenas imprime o registro para STDOUT. Isso é realmente ... na minha opinião, não é uma ótima idéia. Não menos importante porque - ele não imprime a estrutura XML e, portanto, não é realmente XML 'válido' se você tiver mais de um registro (não há um nó "raiz").
Então, eu preferiria: realizar exatamente o que você está perguntando:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
my $twig = XML::Twig -> new -> parsefile ('your_file.xml');
$twig -> set_pretty_print('indented_a');
foreach my $record ( $twig -> get_xpath ( './record' ) ) {
if ( not $record -> findnodes ( './/keyword[string()="SEARCH"]' ) ) {
$record -> delete;
}
}
open ( my $output, '>', "output.txt" ) or die $!;
print {$output} $twig -> sprint;
close ( $output );
Em vez disso - inverte a lógica e exclui (da estrutura de dados analisada na memória) os registros que você não quer e imprime toda a nova estrutura (incluindo cabeçalhos XML) em uma nova arquivo chamado "output.txt".