web-dev-qa-db-fra.com

SED: plusieurs modèles sur la même ligne, comment faire correspondre / analyser le premier

J'ai un fichier, qui contient des données de numéro de téléphone, et aussi des trucs inutiles. J'essaie d'analyser les chiffres, et lorsqu'il n'y a qu'un seul numéro de téléphone/ligne, ce n'est pas un problème. Mais quand j'ai plusieurs numéros, sed correspond au dernier (même si partout où il est dit qu'il ne devrait correspondre qu'au premier motif?), Et je ne peux pas sortir d'autres chiffres ..

Mon data.txt:

bla bla bla NUM:09011111111 bla bla bla bla NUM:08022222222 bla bla bla

Lorsque j'analyse les données, mon idée était d'abord de supprimer tous les "initiaux" "bla bla bla" devant le premier numéro de téléphone (donc je recherche la première occurrence de 'NUM:'), puis je supprime tous les trucs après le numéro de téléphone et obtenez le numéro. Après cela, je veux analyser l'occurrence suivante à partir de la chaîne restante.

Alors maintenant, quand j'essaie de le séduire, j'ai toujours le dernier numéro sur la ligne:

>sed 's/.*NUM://' data.txt
08022222222 bla bla bla
> 

Je voudrais principalement comprendre ce qui ne va pas avec ma compréhension du SED. Bien sûr, des suggestions plus efficaces sont les bienvenues! Ma commande sed ne dit-elle pas de remplacer toutes les choses avant 'NUM:' par '' (vide)? Pourquoi correspond-il toujours à la dernière occurrence?

Merci!

16
julumme

Cela pourrait fonctionner pour vous:

echo "bla bla bla NUM:09011111111 bla bla bla bla NUM:08022222222 bla bla bla" |
sed 's/NUM:/\n&/g;s/[^\n]*\n\(NUM:[0-9]*\)[^\n]*/\1 /g;s/.$//'
NUM:09011111111 NUM:08022222222

Le problème que vous rencontrez est de comprendre que le .* est gourmand, c'est-à-dire qu'il correspond à la correspondance la plus longue pas la première correspondance. En plaçant un caractère unique (\n sed l'utilise comme délimiteur de ligne, il ne peut donc pas exister dans la ligne) devant la chaîne qui nous intéresse (NUM:...) et en supprimant tout ce qui n'est pas ce caractère unique [^\n]* suivi du caractère unique \n, nous avons effectivement divisé la chaîne en morceaux gérables.

22
potong

Comme vous le savez maintenant, les expressions rationnelles sed sont gourmandes et, pour autant que je sache, ne peuvent pas être rendues non gourmandes.

Deux alternatives qui n'ont pas été évoquées jusqu'à présent sont d'utiliser simplement d'autres outils pour ce type de correspondance/extraction.

Vous pouvez utiliser Perl en remplacement de sed avec le -pe paramètres. Il prend en charge le ? modificateur non gourmand:

$ Perl -pe 's/.*?NUM://' data.txt
09011111111 bla bla bla bla NUM:08022222222 bla bla bla

Vous pouvez utiliser le -o option pour GNU grep pour obtenir uniquement les bits de vos données qui correspondent à l'expression régulière:

$ egrep -o 'NUM:[0-9]*' data.txt 
NUM:09011111111
NUM:08022222222
12
Eduardo Ivanec

Si un nombre est défini par des chiffres suivant un NUM::

sed -n -e 's/$/\n/' -e ':begin' \
  -e 's/\(NUM:[0-9][0-9]*\)\(.*\)\n\(.*\)/\2\n\3 \1/' \
  -e 'tbegin' -e 's/.*\n //' -e '/NUM/p'

Ce que cela signifie:

  1. Mettez un \n à la fin de la ligne pour servir de marqueur.
  2. Essayez de trouver un nombre avant le marqueur et mettez-le à la fin de la ligne (après le marqueur).
  3. Si un numéro a été trouvé, passez à 2 ci-dessus.
  4. Lorsqu'il ne reste aucun numéro avant le marqueur, supprimez tout avant les numéros.
  5. Si un numéro est sur la ligne, imprimez-le (pour gérer le cas où aucun numéro n'est trouvé.

Cela peut également être fait dans l'autre sens, en laissant d'abord tomber les lignes sans chiffres:

sed  -e '/NUM/!d' -e 's/$/\n/' -e ':begin' \
  -e 's/\(NUM:[0-9][0-9]*\)\(.*\)\n\(.*\)/\2\n\3 \1/' \
  -e 'tbegin' -e 's/.*\n //'
3
jfg956

Vous pouvez utiliser ce modèle:

sed -r 's/^(.*NUM:)(.*NUM:.*)$/\2/'
0
kev