Eu tenho um arquivo parecido com este:
<table name="content_analyzer" primary-key="id">
<type="global" />
</table>
<table name="content_analyzer2" primary-key="id">
<type="global" />
</table>
<table name="content_analyzer_items" primary-key="id">
<type="global" />
</table>
Preciso extrair qualquer coisa entre as aspas a seguir name=
, ou seja content_analyzer
, content_analyzer2
e content_analyzer_items
.
Estou fazendo isso em uma máquina Linux, então uma solução usando sed, perl, grep ou bash é adequada.
regex
perl
sed
html-parsing
text-extraction
wrangler
fonte
fonte
Respostas:
Visto que você precisa combinar o conteúdo sem incluí-lo no resultado (deve corresponder,
name="
mas não é parte do resultado desejado), alguma forma de correspondência de largura zero ou captura de grupo é necessária. Isso pode ser feito facilmente com as seguintes ferramentas:Perl
Com o Perl, você pode usar a
n
opção de fazer um loop linha por linha e imprimir o conteúdo de um grupo de captura se corresponder a:perl -ne 'print "$1\n" if /name="(.*?)"/' filename
GNU grep
Se você tem uma versão melhorada do grep, como GNU grep, você pode ter a
-P
opção disponível. Esta opção habilitará o regex semelhante ao Perl, permitindo que você use o\K
que é um lookbehind abreviado. Ele irá redefinir a posição de correspondência, então qualquer coisa antes de ter largura zero.grep -Po 'name="\K.*?(?=")' filename
A
o
opção faz com que o grep imprima apenas o texto correspondente, ao invés de toda a linha.Vim - Editor de Texto
Outra maneira é usar um editor de texto diretamente. Com o Vim, uma das várias maneiras de fazer isso seria excluir as linhas sem
name=
e, em seguida, extrair o conteúdo das linhas resultantes::v/.*name="\v([^"]+).*/d|%s//\1
Grep padrão
Se você não tiver acesso a essas ferramentas, por algum motivo, algo semelhante pode ser alcançado com o grep padrão. No entanto, sem olhar ao redor, será necessária alguma limpeza mais tarde:
grep -o 'name="[^"]*"' filename
Uma nota sobre como salvar os resultados
Em todos os comandos acima os resultados serão enviados para
stdout
. É importante lembrar que você sempre pode salvá-los direcionando-os para um arquivo anexando:ao final do comando.
fonte
grep
):grep -Po '.*name="\K.*?(?=".*)'
.*
lado, espero que você não fique com raiva de mim. Eu gostaria de perguntar, você vê algum benefício na combinação não gananciosa em relação a "qualquer coisa exceto"
"? Não leve isso como uma luta, estou apenas curioso e não sou um especialista em regex. Além disso, a\K
dica, muito legal. Obrigado Dennis..*
, você pode fazergrep -Po '(?<=name=").*?(?=")'
. O\K
pode ser usado para abreviar, mas é realmente necessário apenas se a correspondência à sua esquerda tiver comprimento variável. Em casos como esse, a razão para usar lookarounds é bastante óbvia. Operações desagradáveis parecem um pouco mais organizadas ([^"]*
versus.*?
e você não precisa repetir o caractere âncora. Não sei sobre velocidade. Isso depende muito do contexto, eu acho. Espero que seja útil.\K
(depois de pesquisar sobre ele) e removi o.*
foi o mesmo: torná-lo bonito (mais simples). E nunca pensei em usar em.*?
vez do "jeito tradicional" que aprendi em algum lugar. Mas não ganancioso aqui realmente faz sentido. Obrigado Dennis, melhores votos.A expressão regular seria:
.+name="([^"]+)"
Então o agrupamento seria no \ 1
fonte
Se você estiver usando Perl, baixe um módulo para analisar o XML: XML :: Simple , XML :: Twig ou XML :: LibXML . Não reinvente a roda.
fonte
<type="global"
por exemplo), então a maioria dos analisadores XML simplesmente reclama e morre.Um analisador HTML deve ser usado para esse propósito, em vez de expressões regulares. Um programa Perl que faz uso de
HTML::TreeBuilder
:Programa
#!/usr/bin/env perl use strict; use warnings; use HTML::TreeBuilder; my $tree = HTML::TreeBuilder->new_from_file( \*DATA ); my @elements = $tree->look_down( sub { defined $_[0]->attr('name') } ); for (@elements) { print $_->attr('name'), "\n"; } __DATA__ <table name="content_analyzer" primary-key="id"> <type="global" /> </table> <table name="content_analyzer2" primary-key="id"> <type="global" /> </table> <table name="content_analyzer_items" primary-key="id"> <type="global" /> </table>
Resultado
fonte
isso poderia fazer isso:
perl -ne 'if(m/name="(.*?)"/){ print $1 . "\n"; }'
fonte
Aqui está uma solução usando HTML tidy & xmlstarlet:
htmlstr=' <table name="content_analyzer" primary-key="id"> <type="global" /> </table> <table name="content_analyzer2" primary-key="id"> <type="global" /> </table> <table name="content_analyzer_items" primary-key="id"> <type="global" /> </table> ' echo "$htmlstr" | tidy -q -c -wrap 0 -numeric -asxml -utf8 --merge-divs yes --merge-spans yes 2>/dev/null | sed '/type="global"/d' | xmlstarlet sel -N x="http://www.w3.org/1999/xhtml" -T -t -m "//x:table" -v '@name' -n
fonte
Ops, o comando sed deve preceder o comando tidy, é claro:
echo "$htmlstr" | sed '/type="global"/d' | tidy -q -c -wrap 0 -numeric -asxml -utf8 --merge-divs yes --merge-spans yes 2>/dev/null | xmlstarlet sel -N x="http://www.w3.org/1999/xhtml" -T -t -m "//x:table" -v '@name' -n
fonte
Se a estrutura do seu xml (ou texto em geral) for fixa, a maneira mais fácil é usando
cut
. Para o seu caso específico:echo '<table name="content_analyzer" primary-key="id"> <type="global" /> </table> <table name="content_analyzer2" primary-key="id"> <type="global" /> </table> <table name="content_analyzer_items" primary-key="id"> <type="global" /> </table>' | grep name= | cut -f2 -d '"'
fonte