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
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
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ésultatJ'ai écrit un outil pour moi-même qui facilite la tâche
rip 'abc(\d+)xyz' '$1'
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
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).
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.
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.
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]*$//'