it-swarm.dev

Come estrarre la stringa seguendo un modello con grep, regex o perl

Ho un file che assomiglia a questo:

<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>

Ho bisogno di estrarre qualsiasi cosa tra le virgolette che seguono name=, cioè, content_analyzer, content_analyzer2 e content_analyzer_items.

Lo sto facendo su una macchina Linux, quindi una soluzione che usa sed, Perl, grep o bash va bene.

68
wrangler

Dal momento che è necessario abbinare il contenuto senza includerlo nel risultato (necessario Match name=" ma non fa parte del risultato desiderato) è richiesta una qualche forma di Corrispondenza a larghezza zero o acquisizione di gruppo. Questo può essere fatto Facilmente con i seguenti strumenti:

Perl

Con Perl è possibile utilizzare l'opzione n per eseguire il loop riga per riga e stampare Il contenuto di un gruppo di acquisizione se corrisponde a:

Perl -ne 'print "$1\n" if /name="(.*?)"/' filename

GNU grep

Se hai una versione migliorata di grep, come GNU grep, potresti avere L'opzione -P disponibile. Questa opzione abiliterà espressioni regolari Perl-like, Che ti permetteranno di usare \K che è una scorciatoia. Reimposta La posizione della partita, quindi qualsiasi cosa prima che sia zero-width.

grep -Po 'name="\K.*?(?=")' filename

L'opzione o fa in modo che grep stampi solo il testo con corrispondenza, invece della riga intera

Vim - Editor di testo

Un altro modo è usare direttamente un editor di testo. Con Vim, uno dei Vari modi per ottenere ciò sarebbe eliminare le righe senza name= e quindi estrarre il contenuto dalle linee risultanti:

:v/name=/d
:%s/\v.*name\="([^"]+)".*/\1

Grep standard

Se non si ha accesso a questi strumenti, per qualche motivo, qualcosa di simile a Potrebbe essere raggiunto con grep standard. Tuttavia, senza l'aspetto Attorno ad esso sarà necessario un po 'di pulizia in seguito:

grep -o 'name="[^"]*"' filename

Una nota sul salvataggio dei risultati

In tutti i comandi sopra i risultati verranno inviati a stdout. È importante ricordare che è sempre possibile salvarli collegandoli a un file Aggiungendo:

> result

alla fine del comando.

126
sidyll

Se stai usando Perl, scarica un modulo per analizzare XML: XML ​​:: Simple , XML ​​:: Twig , o XML ​​:: LibXML . Non reinventare la ruota.

5
shawnhcorey

L'espressione regolare sarebbe:

.+name="([^"]+)"

Quindi il raggruppamento si troverebbe nel\1 

5
Matt Shaver

Un parser HTML dovrebbe essere usato per questo scopo piuttosto che espressioni regolari. Un programma Perl che utilizza HTML::TreeBuilder :

Programma

#!/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>

Produzione

content_analyzer
content_analyzer2
content_analyzer_items
4

Ecco una soluzione che utilizza HTML tidy e 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
2
mitma

questo potrebbe farlo:

Perl -ne 'if(m/name="(.*?)"/){ print $1 . "\n"; }'
2
Benoit

Oops, il comando sed deve precedere naturalmente il comando ordinato:

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
1
mitma

Se la struttura del tuo xml (o del testo in generale) è fissa, il modo più semplice è usare cut. Per il tuo caso specifico:

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 '"'
0
Carlos Lindado