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']
>>>
RanRag
la source
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'
tripleee
la source
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]+'
mVChr
la source
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.

Suspendu jusqu'à nouvel ordre.
la source
J'ai essayé sed -n '/[0-9]\+G[0-9]\+/p'. Maintenant, il imprime juste la chaîne entière
RanRag
@Noob: vous devrez utiliser la substitution pour exclure les pièces que vous ne souhaitez pas imprimer .
Suspendu jusqu'à nouvel ordre.
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.

Zsolt Botykai
la source
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
Tim Savannah
la source
Si cela utilise une expression régulière standard, les crochets autour \dsont complètement superflus.
tripleee