Comment extraire du texte d'une chaîne en utilisant sed?


95

Mon exemple de chaîne est le suivant:

This is 02G05 a test string 20-Jul-2012

Maintenant, à partir de la chaîne ci-dessus, je veux extraire 02G05. Pour cela j'ai essayé la regex suivante avec sed

$ echo "This is 02G05 a test string 20-Jul-2012" | sed -n '/\d+G\d+/p'

Mais la commande ci-dessus n'imprime rien et la raison pour laquelle je pense est qu'elle n'est pas capable de faire correspondre quoi que ce soit avec le modèle que j'ai fourni à sed.

Donc, ma question est de savoir ce que je fais de mal ici et comment y remédier.

Quand j'essaye la chaîne et le modèle ci-dessus avec python, j'obtiens mon résultat

>>> re.findall(r'\d+G\d+',st)
['02G05']
>>>

6
Python ne l'est certainement pas sed. Leurs saveurs regex sont assez différentes.
— tripleee

Réponses:


91

Le modèle \dn'est peut-être pas pris en charge par votre sed. Essayez [0-9]ou à la [[:digit:]]place.

Pour imprimer uniquement la correspondance réelle (et non la ligne de correspondance entière), utilisez une substitution.

sed -n 's/.*\([0-9][0-9]*G[0-9][0-9]*\).*/\1/p'

6
Merci, cela a bien fonctionné. Mais j'ai une question pourquoi .*est nécessaire avec votre regex parce que quand j'essaye, sed -n 's/\([0-9]\+G[0-9]\+\)/\1/p'il imprime juste la ligne entière.
— RanRag

7
C'est pourquoi, n'est-ce pas? Remplacez ce qui vient avant et après le match par norhing, puis imprimez toute la ligne.
— tripleee

1
@tripleee Ceci n'imprime 2G05pas seulement 02G05. L'expression qui fonctionne est's/.*\([0-9][0-9]G[0-9][0-9]*\).*/\1/p'
— Kshitiz Sharma

1
Cela le code en dur à exactement deux chiffres. Quelque chose comme sed -n 's/\(.*[^0-9]\)\?\([0-9][0-9]*G[0-9][0-9]*\).*/\2/p'serait plus général. (Je suppose que vos sedsupports \?pour zéro ou un événement.)
— tripleee

Voir aussi stackoverflow.com/a/48898886/874188 pour savoir comment remplacer divers autres Perl commun échappe comme \w, \s, etc.
— tripleee

98

Que diriez-vous d'utiliser grep -E?

echo "This is 02G05 a test string 20-Jul-2012" | grep -Eo '[0-9]+G[0-9]+'

3
+1 C'est plus simple et traitera également correctement le cas de plusieurs correspondances sur la même ligne. Un sedscript complexe pourrait être conçu pour ce cas, mais pourquoi s'embêter?
— tripleee

egreputilise une expression régulière étendue, sedet greputilise une expression régulière standard, egrepou grep -eou sed -Eutilise une expression régulière étendue, et le code python dans la question utilise PCRE, (expression régulière commune perl) GNU grep peut utiliser PCRE avec l' -Poption.
— Felipe Buccioni

@FelipeBuccioni en fait qui devrait être egrepou grep -Eoused -r
— SensorSmith

Pour une (première) seule correspondance, ajoutez `| head -1` (sans backticks), selon cette réponse à une autre question.
— SensorSmith

1
grepdoit -m 1s'arrêter après le premier match.
— tripleee

5

sedne reconnaît pas \d, utilisez à la [[:digit:]]place. Vous devrez également échapper +ou utiliser le -rcommutateur ( -Esous OS X).

Notez que cela [0-9]fonctionne également pour les chiffres arabes-hindous.


J'ai essayé sed -n '/[0-9]\+G[0-9]\+/p'. Maintenant, il imprime juste la chaîne entière
— RanRag


5

Essayez plutôt ceci:

echo "This is 02G05 a test string 20-Jul-2012" | sed 's/.* \([0-9]\+G[0-9]\+\) .*/\1/'

Mais notez que s'il y a deux motifs sur une ligne, il imprime le 2ème.


Ou plus généralement le dernier s'il y a plusieurs correspondances.
— tripleee

0

Essayez d'utiliser rextract . Il vous permettra d'extraire du texte à l'aide d'une expression régulière et de le reformater.

Exemple:

$ echo "This is 02G05 a test string 20-Jul-2012" | ./rextract '([\d]+G[\d]+)' '${1}'

2G05

Si cela utilise une expression régulière standard, les crochets autour \dsont complètement superflus.
— tripleee
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.