web-dev-qa-db-fra.com

Comment extraire une chaîne suivant un motif avec grep, regex ou perl

J'ai un fichier qui ressemble à quelque chose comme ça:

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

J'ai besoin d'extraire quoi que ce soit entre les guillemets qui suivent name=, i.e., content_analyzer, content_analyzer2 et content_analyzer_items.

Je le fais sur une machine Linux, donc une solution utilisant sed, Perl, grep ou bash convient parfaitement.

68
wrangler

Etant donné que vous devez faire correspondre le contenu sans l'inclure dans le résultat (doit Correspondre à name=" mais ne fait pas partie du résultat souhaité), une forme de Une correspondance de largeur nulle ou une capture de groupe est requise. Cela peut être fait facilement avec les outils suivants:

Perl

Avec Perl, vous pouvez utiliser l’option n pour boucler ligne par ligne et imprimer Le contenu d’un groupe de capture s’il correspond:

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

GNU grep

Si vous avez une version améliorée de grep, telle que GNU grep, il est possible que L'option -P soit disponible. Cette option activera les expressions rationnelles de type Perl, , Vous permettant d'utiliser \K, qui est un raccourci. Cela réinitialisera La position de la correspondance, donc tout ce qui la précède a une largeur nulle.

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

L’option o permet à grep d’imprimer uniquement le texte correspondant, au lieu de la ligne entière

Vim - Éditeur de texte

Une autre méthode consiste à utiliser directement un éditeur de texte. Avec Vim, l’une des manières De procéder serait de supprimer des lignes sans name= et d’extraire ensuite le contenu des lignes résultantes:

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

Grep standard

Si, pour une raison quelconque, vous n’avez pas accès à ces outils, un résultat similaire pourrait être obtenu avec grep standard. Cependant, sans le look Autour, il faudra nettoyer plus tard:

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

Une note sur la sauvegarde des résultats

Dans toutes les commandes ci-dessus, les résultats seront envoyés à stdout. Il est important de se rappeler que vous pouvez toujours les sauvegarder en le connectant à un fichier En ajoutant:

> result

à la fin de la commande.

126
sidyll

Si vous utilisez Perl, téléchargez un module pour analyser le XML: XML ​​:: Simple , XML ​​:: Twig ou XML ​​:: LibXML . Ne réinventez pas la roue.

5
shawnhcorey

L'expression régulière serait:

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

Ensuite, le regroupement serait dans le\1 

5
Matt Shaver

Un analyseur HTML devrait être utilisé à cette fin plutôt que des expressions régulières. Un programme Perl qui utilise HTML::TreeBuilder :

Programme

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

Sortie

content_analyzer
content_analyzer2
content_analyzer_items
4
Alan Haggai Alavi

Voici une solution utilisant HTML nettoie & 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

cela pourrait le faire:

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

Oups, la commande sed doit précéder la commande bien rangée:

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

Si la structure de votre XML (ou du texte en général) est corrigée, le moyen le plus simple consiste à utiliser cut. Pour votre cas particulier:

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