J'ai un fichier qui ressemble à ceci:
<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 tout ce qui se trouve dans les guillemets qui suivent name=
, c'est-à-dire content_analyzer
, content_analyzer2
et content_analyzer_items
.
Je fais cela sur une machine Linux, donc une solution utilisant sed, perl, grep ou bash est bien.
regex
perl
sed
html-parsing
text-extraction
cow-boy
la source
la source
Réponses:
Puisque 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 correspondance de largeur nulle ou de capture de groupe est requise. Cela peut être fait facilement avec les outils suivants:Perl
Avec Perl, vous pouvez utiliser l'
n
option 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
Grep GNU
Si vous avez une version améliorée de grep, telle que GNU grep, vous pouvez avoir l'
-P
option disponible. Cette option activera les expressions régulières de type Perl, vous permettant d'utiliser\K
ce qui est un raccourci vers l'arrière. Il réinitialisera la position de correspondance, donc tout ce qui est avant sa largeur zéro.grep -Po 'name="\K.*?(?=")' filename
L'
o
option fait que grep n'imprime que le texte correspondant, au lieu de la ligne entière.Vim - Éditeur de texte
Une autre façon consiste à utiliser directement un éditeur de texte. Avec Vim, l'une des différentes manières d'y parvenir serait de supprimer les lignes sans
name=
puis d'extraire le contenu des lignes résultantes::v/.*name="\v([^"]+).*/d|%s//\1
Grep standard
Si vous n'avez pas accès à ces outils, pour une raison quelconque, quelque chose de similaire pourrait être réalisé avec grep standard. Cependant, sans le regard autour, il nécessitera un nettoyage plus tard:
grep -o 'name="[^"]*"' filename
Une note sur l'enregistrement 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 enregistrer en le redirigeant vers un fichier en ajoutant:à la fin de la commande.
la source
grep
):grep -Po '.*name="\K.*?(?=".*)'
.*
j'ai laissé les deux de côté, j'espère que vous ne vous fâchez pas contre moi. Je voudrais demander, voyez-vous des avantages d'un match non gourmand par rapport à «autre chose"
»? Ne prenez pas cela comme un combat, je suis juste curieux et je ne suis pas un expert en regex. Aussi, le\K
conseil, vraiment sympa. Merci Dennis..*
, vous pouvez le fairegrep -Po '(?<=name=").*?(?=")'
. Le\K
peut être utilisé pour un raccourci, mais il n'est vraiment nécessaire que si la correspondance à sa gauche est de longueur variable. Dans des cas comme celui-ci, la raison d'utiliser des lookarounds est assez évidente. Les opérations non gênées semblent un peu plus soignées ([^"]*
par rapport à.*?
et vous n'avez pas à répéter le caractère d'ancrage. Je ne sais pas à propos de la vitesse. Cela dépend beaucoup du contexte, je pense. J'espère que c'est utile.\K
(après avoir fait des recherches à ce sujet) et supprimé le.*
était la même: lui donner un aspect joli (plus simple). Et je n'ai jamais pensé à utiliser.*?
au lieu de la "manière traditionnelle" que j'ai appris quelque part. Mais non gourmand ici a vraiment du sens. Merci Dennis, meilleurs voeux.L'expression régulière serait:
.+name="([^"]+)"
Ensuite, le regroupement serait dans le \ 1
la source
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.
la source
<type="global"
par exemple), donc la plupart des analyseurs XML se plaignent et meurent.Un analyseur HTML doit ê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>
Production
la source
cela pourrait le faire:
perl -ne 'if(m/name="(.*?)"/){ print $1 . "\n"; }'
la source
Voici une solution utilisant HTML tidy et 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
la source
Oups, la commande sed doit bien sûr précéder la commande tidy:
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
la source
Si la structure de votre xml (ou du texte en général) est fixe, le moyen le plus simple est d'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 '"'
la source