web-dev-qa-db-fra.com

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

Je vois beaucoup d'exemples et de pages de manuel expliquant comment effectuer des opérations telles que la recherche et le remplacement à l'aide de sed, awk ou gawk.

Mais dans mon cas, j'ai une expression régulière que je veux exécuter sur un fichier texte pour extraire une valeur spécifique. Je ne veux pas faire de recherche-remplacer. Ceci est appelé de 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 ne vois pas comment appeler correctement sed/awk/gawk. Ce que j’espérais faire, c’est de mon script bash:

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

Les choses que j'ai essayées incluent:

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
95
Stéphane

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

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

Pour faire correspondre au moins un caractère numérique sans +, J'utiliserais:

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

Vous pouvez utiliser sed pour le faire

 sed -rn 's/.*abc([0-9]+)xyz.*/\1/gp'
  • -n n'imprime pas la ligne résultante
  • -r _ cela fait en sorte que vous ne puissiez pas échapper au groupe de capture parens().
  • \1 la correspondance du groupe de capture
  • /g match global
  • /p imprimer le résultat

J'ai écrit un outil pour moi-même qui facilite la tâche

rip 'abc(\d+)xyz' '$1'
32
Ilia Choly

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

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

Cela exécute Perl, l'option -n Indique à Perl de lire ligne par ligne à partir de STDIN et d'exécuter le code. L'option -e Spécifie l'instruction à exécuter.

L'instruction exécute une expression rationnelle sur la ligne lue et, si elle correspond, affiche le contenu du premier ensemble de supports ($1).

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

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

17
PP.

Si votre version de grep le prend en charge, vous pouvez utiliser le -o option à imprimer niquement la partie de toute ligne correspondant à votre expression rationnelle.

Sinon, voici le meilleur sed que je pourrais trouver:

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

... qui supprime/saute sans chiffres et, pour les lignes restantes, supprime tous les caractères non numériques de début et de fin. (Je suppose seulement que votre intention est d'extraire le nombre de chaque ligne qui en contient une).

Le problème avec quelque chose comme:

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

.... ou

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

... est-ce que sed ne supporte que les correspondances "gloutonnes" ... donc le premier. * correspondra au reste de la ligne. Sauf si nous pouvons utiliser une classe de caractères inversée pour obtenir une correspondance non gourmande ... ou une version de sed avec des extensions compatibles avec Perl ou autres, nous ne pouvons pas extraire une correspondance de motif précise avec l'espace motif (une ligne).

5
Jim Dennis

Vous pouvez utiliser awk avec match() pour accéder au groupe capturé:

$ awk 'match($0, /abc([0-9]+)xyz/, matches) {print matches[1]}' file
12345

Cela tente de faire correspondre le modèle abc[0-9]+xyz. Si tel est le cas, il stocke ses tranches dans le tableau matches, dont le premier élément est le bloc [0-9]+. Puisque match() renvoie la position du caractère, ou l’index, du début de la sous-chaîne (1, si elle commence au début de la chaîne) , il déclenche l'action print.


Avec grep, vous pouvez utiliser un regard en arrière et un regard en avant:

$ grep -oP '(?<=abc)[0-9]+(?=xyz)' file
12345

$ grep -oP 'abc\K[0-9]+(?=xyz)' file
12345

Ceci vérifie le modèle [0-9]+ Quand il se produit dans abc et xyz et imprime simplement les chiffres.

3
fedorqui

Perl est la syntaxe la plus propre, mais si vous n'avez pas Perl (pas toujours là, je comprends), alors le seul moyen d'utiliser gawk et les composants d'une expression régulière est d'utiliser la fonction gensub.

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

la sortie de l'exemple de fichier d'entrée sera

12345

Remarque: gensub remplace l'intégralité de la regex entière (entre les //), vous devez donc placer le. * Avant et après le ([0-9] +) pour supprimer le texte avant et après le nombre indiqué dans la substitution.

2
Mark Lakata

Si vous voulez sélectionner des lignes, supprimez les bits que vous ne voulez pas:

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

En gros, il sélectionne les lignes souhaitées avec egrep, puis utilise sed pour supprimer les bits avant et après le nombre.

Vous pouvez voir cela 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: évidemment si votre situation réelle est plus complexe, les RE devront me modifier. Par exemple, si vous avez toujours un seul numéro 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]*$//'
1
paxdiablo