sed: imprimer uniquement le groupe correspondant

133

Je veux saisir les deux derniers nombres (un entier, un flottant; suivi d'un espace blanc facultatif) et les imprimer uniquement.

Exemple:

foo bar <foo> bla 1 2 3.4

Devrait imprimer:

2 3.4

Jusqu'à présent, j'ai ce qui suit:

sed -n  's/\([0-9][0-9]*[\ \t][0-9.]*[\ \t]*$\)/replacement/p' 

me donnera

foo bar <foo> bla 1 replacement

Cependant, si j'essaye de le remplacer par le groupe 1, la ligne entière est imprimée.

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

Comment puis-je imprimer uniquement la section de la ligne qui correspond à l'expression régulière dans le groupe?

mort
la source

Réponses:

138

.*Faites correspondre toute la ligne, alors ajoutez un au début de votre regex. Cela provoque le remplacement de la ligne entière par le contenu du groupe

echo "foo bar <foo> bla 1 2 3.4" |
 sed -n  's/.*\([0-9][0-9]*[\ \t][0-9.]*[ \t]*$\)/\1/p'
2 3.4
iruvar
la source
38
J'ai dû ajouter l' -roption ou `--regexp-extended` sinon j'obtenais invalid reference \1 on l'erreur RHS` de la commande s.
Daniel Sokolowski
15
@DanielSokolowski Je pense que vous obtenez cette erreur si vous utilisez (et )au lieu de \(et \).
Daniel Darabos
3
Pensez également à ajouter .*à la fin de l'expression rationnelle si la chaîne que vous souhaitez extraire n'est pas toujours à la fin de la ligne.
Teemu Leisti
3
Cela ne fonctionnera pas pour moi car il .*est gourmand et sed n'a pas de non-gourmand.*?
sondra.kinsey
@DanielDarabos Il suffit de mentionner cela (et )ne soulèvera pas d'erreur dans ubuntu 16.04. Je pense donc que ce commentaire est dépassé.
Li haonan
72

grep est le bon outil pour l'extraction.

en utilisant votre exemple et votre regex:

kent$  echo 'foo bar <foo> bla 1 2 3.4'|grep -o '[0-9][0-9]*[\ \t][0-9.]*[\ \t]*$'
2 3.4
Kent
la source
12
idéal pour tout le groupe, bien que sed soit nécessaire pour les groupes individuels
jozxyqk
grep -o ne porte pas sur les systèmes exécutant msysgit mais sed le fait.
cchamberlain
Voir la question liée par @jozxyqk pour une réponse qui utilise l'anticipation et l'anticipation pour résoudre ce problème avec grep.
Joachim Breitner
Vous pouvez extraire un groupe d'un modèle avec des grep -oappels canalisés . stackoverflow.com/a/58314379/117471
Bruno Bronosky
12

Et pour encore une autre option, j'irais avec awk!

echo "foo bar <foo> bla 1 2 3.4" | awk '{ print $(NF-1), $NF; }'

Cela divisera l'entrée (j'utilise STDIN ici, mais votre entrée pourrait facilement être un fichier) sur des espaces, puis imprimera le dernier champ mais un, puis le dernier champ. le$NF variables contiennent le nombre de champs trouvés après avoir explosé sur les espaces.

L'avantage de ceci est que peu importe si ce qui précède les deux derniers champs change, tant que vous ne voulez que les deux derniers, cela continuera à fonctionner.

chooban
la source
3

La commande de coupe est conçue pour cette situation exacte. Il "coupera" sur n'importe quel délimiteur et vous pourrez alors spécifier les morceaux à sortir.

Par exemple: echo "foo bar <foo> bla 1 2 3.4" | cut -d " " -f 6-7

Se traduira par la sortie de: 2 3.4

-d définit le délimiteur

-f sélectionne la plage de «champs» à afficher, dans ce cas, il s'agit du 6ème au 7ème morceaux de la chaîne d'origine. Vous pouvez également spécifier la plage sous forme de liste, par exemple 6,7.

carlin.scott
la source
Pour imprimer uniquement certaines colonnes, awk '{ print $2" "$6 }'
pointez
@nurettin Je pense que votre commentaire était peut-être destiné à l'une des réponses les plus difficiles.
carlin.scott
J'ai essayé de couper quand j'ai visité cette page et j'ai réalisé ses limites et j'ai décidé d'écrire une version plus généralisée dans awk à la place comme un commentaire pour améliorer la qualité de ce message.
nurettin le
1
Ouais, je pense que cela appartient à une réponse différente impliquant awk. La commande cut pour faire ce que vous avez écrit est:cut -d " " -f 2,6
carlin.scott
ah, je ne savais pas ça, je pensais que vous ne pouviez donner que des plages. Merci pour ça.
nurettin le
2

Je suis d'accord avec @kent pour dire que c'est bien adapté grep -o. Si vous avez besoin d'extraire un groupe dans un pattern, vous pouvez le faire avec un 2nd grep.

# To extract \1 from /xx([0-9]+)yy/
$ echo "aa678bb xx123yy xx4yy aa42 aa9bb" | grep -Eo 'xx[0-9]+yy' | grep -Eo '[0-9]+'
123
4

# To extract \1 from /a([0-9]+)b/
$ echo "aa678bb xx123yy xx4yy aa42 aa9bb" | grep -Eo 'a[0-9]+b' | grep -Eo '[0-9]+'
678
9
Bruno Bronosky
la source