101 votes

comment utiliser sed, awk ou gawk pour n'imprimer que ce qui correspond ?

Je vois beaucoup d'exemples et de pages de manuel sur la façon de faire des choses comme rechercher et remplacer en utilisant sed, awk ou gawk.

Mais dans mon cas, j'ai une expression régulière que je veux exécuter contre un fichier texte pour en extraire une valeur spécifique. Je ne veux pas faire de recherche et de remplacement. Ceci est appelé depuis bash. Prenons un exemple :

Exemple d'expression régulière :

.*abc([0-9]+)xyz.*

Exemple de fichier d'entrée :

a
b
c
abc12345xyz
a
b
c

Aussi simple que cela puisse paraître, je n'arrive pas à trouver comment appeler sed/awk/gawk correctement. Ce que j'espérais faire, c'est à partir de mon bash script ont :

myvalue=$( sed <...something...> input.txt )

Parmi les choses que j'ai essayées, il y a :

sed -e 's/.*([0-9]).*/\\1/g' example.txt # extracts the entire input file
sed -n 's/.*([0-9]).*/\\1/g' example.txt # extracts nothing

44voto

mouviciel Points 36624

Mon sed (Mac OS X) ne fonctionnait pas avec + . J'ai essayé * à la place et j'ai ajouté p pour l'impression de la correspondance :

sed -n 's/^.*abc\([0-9]*\)xyz.*$/\1/p' example.txt

Pour la correspondance d'au moins un caractère numérique sans + que j'utiliserais :

sed -n 's/^.*abc\([0-9][0-9]*\)xyz.*$/\1/p' example.txt

0 votes

Merci, cela a marché pour moi aussi une fois que j'ai utilisé * au lieu de +.

2 votes

...et l'option "p" pour imprimer la correspondance, que je ne connaissais pas non plus. Merci encore.

2 votes

Je devais échapper à la + et ensuite ça a marché pour moi : sed -n 's/^.*abc\([0-9]\+\)xyz.*$/\1/p'

18voto

PP. Points 6741

J'utilise perl pour me faciliter la tâche, par exemple.

perl -ne 'print $1 if /.*abc([0-9]+)xyz.*/'

Cela permet d'exécuter Perl, le -n indique à Perl de lire une ligne à la fois depuis STDIN et d'exécuter le code. L'option -e spécifie l'instruction à exécuter.

L'instruction exécute une regexp sur la ligne lue, et si elle correspond, elle imprime le contenu du premier ensemble de parenthèses ( $1 ).

Vous pouvez également faire cela avec plusieurs noms de fichiers à la fin, par exemple.

perl -ne 'print $1 if /.*abc([0-9]+)xyz.*/' example1.txt example2.txt

0 votes

Merci, mais nous n'avons pas accès à perl, c'est pourquoi je demandais sed/awk/gawk.

5voto

Jim Dennis Points 5454

Si votre version de grep le supporte, vous pourriez utiliser le -o option pour imprimer uniquement la partie de toute ligne qui correspond à votre regexp.

Sinon, voici le meilleur sed que j'ai pu trouver :

sed -e '/[0-9]/!d' -e 's/^[^0-9]*//' -e 's/[^0-9]*$//'

... qui supprime/saute les lignes sans chiffres et, pour les lignes restantes, supprime tous les caractères de tête et de queue sans chiffres. (Je suppose seulement que votre intention est d'extraire le numéro de chaque ligne qui en contient un).

Le problème avec quelque chose comme :

sed -e 's/.*\([0-9]*\).*/&/' 

.... ou

sed -e 's/.*\([0-9]*\).*/\1/'

... est que sed ne supporte que la correspondance "gourmande" ... donc le premier .* correspondra au reste de la ligne. À moins que nous ne puissions utiliser une classe de caractères niés pour obtenir une correspondance non avide ... ou une version de sed avec des extensions compatibles avec Perl ou d'autres extensions de ses regex, nous ne pouvons pas extraire une correspondance précise du motif à partir de l'espace du motif (une ligne).

0 votes

Vous pouvez simplement combiner deux de vos sed commandes de cette manière : sed -n 's/[^0-9]*\([0-9]\+\).*/\1/p'

0 votes

Auparavant, je ne connaissais pas l'option -o de grep. C'est bon à savoir. Mais elle imprime l'ensemble de la correspondance, pas les "(...)". Ainsi, si vous recherchez "abc([[:digit :]]+)xyz", vous obtenez "abc" et "xyz" ainsi que les chiffres.

0 votes

Merci de m'avoir rappelé grep -o ! J'ai essayé de faire cela avec sed et je me suis débattu avec mon besoin de trouver des correspondances multiples sur certaines lignes. Ma solution est la suivante stackoverflow.com/a/58308239/117471

2voto

Mark Lakata Points 3458

Perl est la syntaxe la plus propre, mais si vous n'avez pas perl (ce n'est pas toujours le cas, je comprends), alors la seule façon d'utiliser gawk et les composants d'une regex est d'utiliser la fonctionnalité gensub.

gawk '/abc[0-9]+xyz/ { print gensub(/.*([0-9]+).*/,"\\1","g"); }' < file

La sortie du fichier d'entrée de l'échantillon sera

12345

Remarque : gensub remplace l'ensemble de la regex (entre les //), vous devez donc mettre le .* avant et après le ([0-9]+) pour vous débarrasser du texte avant et après le numéro dans la substitution.

2 votes

Une solution intelligente et réalisable si vous devez (ou voulez) utiliser gawk. Vous l'avez noté, mais pour être clair : l'awk non-GNU n'a pas gensub(), et donc ne supporte pas ceci.

0 votes

Joli ! Cependant, il serait peut-être préférable d'utiliser match() pour accéder aux groupes capturés. Voir ma réponse pour ça.

1voto

paxdiablo Points 341644

Si vous voulez sélectionner des lignes, enlevez les parties que vous ne voulez pas :

egrep 'abc[0-9]+xyz' inputFile | sed -e 's/^.*abc//' -e 's/xyz.*$//'

Il sélectionne essentiellement les lignes que vous voulez avec egrep et utilise ensuite sed pour enlever les bits avant et après le numéro.

Vous pouvez le voir en action ici :

pax> echo 'a
b
c
abc12345xyz
a
b
c' | egrep 'abc[0-9]+xyz' | sed -e 's/^.*abc//' -e 's/xyz.*$//'
12345
pax> 

Mise à jour : Il est évident que si votre situation réelle est plus complexe, les ERs devront être modifiés. Par exemple, si vous avez toujours un seul chiffre enfoui dans zéro ou plusieurs chiffres non numériques au début et à la fin :

egrep '[^0-9]*[0-9]+[^0-9]*$' inputFile | sed -e 's/^[^0-9]*//' -e 's/[^0-9]*$//'

0 votes

Intéressant... Il n'y a donc pas un moyen simple d'appliquer une expression régulière complexe et de récupérer uniquement ce qui se trouve dans la section (...) ? Car si je vois ce que vous avez fait ici d'abord avec grep puis avec sed, notre situation réelle est beaucoup plus complexe que de laisser tomber "abc" et "xyz". L'expression régulière est utilisée parce que beaucoup de textes différents peuvent apparaître de part et d'autre du texte que je voudrais extraire.

0 votes

Je suis sûr qu'il y a est une meilleure façon de procéder si les ER sont vraiment complexes. Peut-être que si vous fournissiez quelques exemples supplémentaires ou une description plus détaillée, nous pourrions adapter nos réponses en conséquence.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X