Grep peut-il afficher uniquement les mots qui correspondent au modèle de recherche?

685

Existe-t-il un moyen de créer des "mots" de sortie grep à partir de fichiers correspondant à l'expression de recherche?

Si je veux trouver toutes les instances de, disons, "th" dans un certain nombre de fichiers, je peux faire:

grep "th" *

mais la sortie sera quelque chose comme (gras est par moi);

fichier-texte: le chat était assis sur le tapis  
autre-fichier-texte: le renard brun rapide  
encore un autre fichier texte: j'espère que cela l' explique à fond 

Ce que je veux qu'il génère, en utilisant la même recherche, c'est:

the
the
the
this
thoroughly

Est-ce possible en utilisant grep? Ou en utilisant une autre combinaison d'outils?

Neil Baldwin
la source
2
La solution Dan Midwood fonctionne parfaitement et mérite le crédit.
hakish
Existe-t-il un moyen d'imprimer ces mots correspondants sans changer les lignes? La chaîne correspondante doit-elle plutôt rester dans la même ligne?
Linguiste le

Réponses:

958

Essayez grep -o

grep -oh "\w*th\w*" *

Edit: correspondance du commentaire de Phil

De la documentation :

-h, --no-filename
    Suppress the prefixing of file names on output. This is the default
    when there is only  one  file  (or only standard input) to search.
-o, --only-matching
    Print  only  the matched (non-empty) parts of a matching line,
    with each such part on a separate output line.
Dan Midwood
la source
9
@ user181548, L'option grep -o ne fonctionne que pour grep GNU. Donc, si vous n'utilisez pas GNU grep, cela pourrait ne pas fonctionner pour vous.
ksinkar
5
@ABB Cela dépend si vous voulez afficher le nom du fichier correspondant ou non. Je ne sais pas dans quelles conditions il s'affiche et ne s'affiche pas, mais je sais que lorsque j'ai utilisé grep dans un certain nombre de répertoires, il a affiché le chemin d'accès complet pour tous les fichiers correspondants, tandis qu'avec -h, il a simplement affiché le mots correspondants sans aucune spécification sur quel fichier il s'agit. Donc, pour correspondre à la question initiale, je pense que c'est nécessaire dans certaines circonstances.
LokMac
1
J'avais besoin d'une explication de ce que cela "\w*th\w*" *signifie, alors j'ai pensé que je posterais. \west [_ [: alnum:]], donc cela correspond essentiellement à tout "mot" qui contient "th" (car il \wn'inclut pas d'espace). Le * après la section citée est un glob pour lequel les fichiers (ie, correspondant à tous les fichiers de ce répertoire)
jeremysprofile
1
\wn'est généralement pas portable pour grep -E; pour une portabilité correcte, utilisez [[:alnum:]]plutôt le nom de la classe de caractères POSIX (ou [_[:alnum:]]si vous voulez vraiment le soulignement aussi; ou essayez grep -Psi votre plate-forme en dispose).
tripleee
@ABB Étant donné la sortie souhaitée indiquée par l'OP, -hc'est tout à fait nécessaire, je dirais ..?
El Ronnoco
81

Réponse sécurisée à la distribution croisée (y compris Windows MinGW?)

grep -h "[[:alpha:]]*th[[:alpha:]]*" 'filename' | tr ' ' '\n' | grep -h "[[:alpha:]]*th[[:alpha:]]*"

Si vous utilisez des versions plus anciennes de grep (comme 2.4.2) qui n'incluent pas l'option -o. Utilisez ce qui précède. Sinon, utilisez le plus simple pour maintenir la version ci-dessous.

Réponse sécurisée à la distribution croisée Linux

grep -oh "[[:alpha:]]*th[[:alpha:]]*" 'filename'

Pour résumer les -ohsorties, l'expression régulière correspond au contenu du fichier (et non à son nom de fichier), tout comme la façon dont vous vous attendriez à ce que l'expression régulière fonctionne dans vim / etc ... Le mot ou l'expression régulière que vous recherchez alors dépend vous! Tant que vous restez sur POSIX et non sur la syntaxe Perl (voir ci-dessous)

Plus dans le manuel de grep

-o      Print each match, but only the match, not the entire line.
-h      Never print filename headers (i.e. filenames) with output lines.
-w      The expression is searched for as a word (as if surrounded by
         `[[:<:]]' and `[[:>:]]';

La raison pour laquelle la réponse originale ne fonctionne pas pour tout le monde

L'utilisation de \wvarie d'une plateforme à l'autre, car c'est une syntaxe "perl" étendue. En tant que telle, cette installation grep qui est limitée au travail avec les classes de caractères POSIX utilise [[:alpha:]]et non son équivalent perl \w. Voir la page Wikipedia sur l'expression régulière pour en savoir plus

En fin de compte, la réponse POSIX ci-dessus sera beaucoup plus fiable quelle que soit la plate-forme (étant l'original) pour grep

Quant à la prise en charge de grep sans l'option -o, le premier grep sort les lignes pertinentes, le tr divise les espaces en nouvelles lignes, le grep final filtre uniquement pour les lignes respectives.

(PS: je sais que la plupart des plates-formes auraient été corrigées pour \ w .... mais il y a toujours celles qui sont en retard)

Crédit pour la solution de contournement «-o» de @AdamRosenfield réponse

PicoCreator
la source
1
Qu'en est--seulement de travailler dans GNU grep (comme mentionné par ksinkar dans un commentaire sur la réponse acceptée)?
Brilliand
@Brilliand hmm, j'ai du mal à trouver une implémentation linux qui ne prend pas en charge '-o', je peux chercher un moyen de contourner si je sais sur quelle plateforme vérifier.
PicoCreator
@pico L' -ooption n'est pas présente dans le grep windows qui s'installe avec le paquet git (minGW?): "c:\Program Files (x86)\Git\bin\grep" --version grep (GNU grep) 2.4.2
Bruce Peterson
@BrucePeterson j'ai ajouté dans la réponse de contournement d'AdamRosenfield pour -o: Aidez-moi à vérifier si le git de windows inclut tr / sed et sa version. Je peux donc vérifier si cette solution de contournement fonctionne
PicoCreator
@pico: pour GIT: GNU sed version 4.2.1, tr (GNU textutils) 2.0
Bruce Peterson
46

C'est plus simple que vous ne le pensez. Essaye ça:

egrep -wo 'th.[a-z]*' filename.txt #### (Case Sensitive)

egrep -iwo 'th.[a-z]*' filename.txt  ### (Case Insensitive)

Où,

 egrep: Grep will work with extended regular expression.
 w    : Matches only word/words instead of substring.
 o    : Display only matched pattern instead of whole line.
 i    : If u want to ignore case sensitivity.
Abhinandan prasad
la source
2
Cela ne semble rien ajouter aux réponses existantes de plus de 4 ans auparavant.
tripleee
3
@tripleee J'ai trouvé que mon approche était meilleure et simple, j'ai donc posté ceci.
Abhinandan prasad
42

Vous pouvez traduire des espaces en sauts de ligne, puis grep, par exemple:

cat * | tr ' ' '\n' | grep th
Adam Rosenfield
la source
18
pas besoin de chat. tr '' '\ n' <fichier | grep th. Lent pour les gros fichiers.
ghostdog74
Ça n'a pas marché. La sortie contenait toujours le nom de fichier et la ligne entière du fichier contenant la correspondance. Quoi qu'il en soit, l'une des autres solutions proposées a fonctionné. Merci pour votre contribution.
Neil Baldwin
@ ghostdog74: bon point, bien que si vous avez plus de fichiers, vous devrez utiliser cat. @Neil Baldwin: êtes-vous sûr de l'avoir tapé à droite? Lorsqu'il n'y a qu'un seul fichier d'entrée (stdin dans ce cas), grep n'imprime pas le nom du fichier.
Adam Rosenfield
@Adam - oui, désolé Adam, cela fonctionne avec un seul fichier mais pas plusieurs.
Neil Baldwin
4
@ ghostdog74 si la partie lente est à cause de cela tr, il pourrait le faire en greppremier, donc trne serait appliqué qu'aux lignes correspondantes:grep th filename | tr ' ' '\n' | grep th
Carcamano
37

Juste awk, pas besoin d'une combinaison d'outils.

# awk '{for(i=1;i<=NF;i++){if($i~/^th/){print $i}}}' file
the
the
the
this
thoroughly
ghostdog74
la source
8
@AjeetGanga bien, c'est dans le nom
Daerdemandt
11

commande grep pour la correspondance et perl uniquement

grep -o -P 'th.*? ' filename
Raghu
la source
3
Qu'en est-il uniquement de l'affichage du groupe correspondant?
Bishwas Mishra
Cela ne fonctionne pas; il ne le trouvera que thparce que vous avez demandé la répétition la plus courte possible du caractère générique.
tripleee
@tripleee - il n'aura pas ce problème, car il y a un espace inclus à la fin de l'expression régulière. Cependant, il manquera des mots qui n'ont pas d'espaces après eux, par exemple à la fin des lignes.
Ken Williams
8

Je n'étais pas satisfait de la syntaxe difficile à retenir d'awk mais j'ai aimé l'idée d'utiliser un utilitaire pour le faire.

Il semble que ack (ou ack-grep si vous utilisez Ubuntu) peut le faire facilement:

# ack-grep -ho "\bth.*?\b" *

the
the
the
this
thoroughly

Si vous omettez l'indicateur -h, vous obtenez:

# ack-grep -o "\bth.*?\b" *

some-other-text-file
1:the

some-text-file
1:the
the

yet-another-text-file
1:this
thoroughly

En bonus, vous pouvez utiliser l' --outputindicateur pour le faire pour des recherches plus complexes avec à peu près la syntaxe la plus simple que j'ai trouvée:

# echo "bug: 1, id: 5, time: 12/27/2010" > test-file
# ack-grep -ho "bug: (\d*), id: (\d*), time: (.*)" --output '$1, $2, $3' test-file

1, 5, 12/27/2010
Beau
la source
8
cat *-text-file | grep -Eio "th[a-z]+"
Mac marmonnant
la source
2
ou tout simplement grep -Eio "th [az] +" filename
Shayan
3
Peut-être voir aussi Utilisation inutile de cat?
tripleee
4

Pour rechercher tous les mots commençant par "icon-", la commande suivante fonctionne parfaitement. J'utilise ici Ack qui est similaire à grep mais avec de meilleures options et un formatage agréable.

ack -oh --type=html "\w*icon-\w*" | sort | uniq
Sandeep
la source
3

Vous pouvez également essayer pcregrep . Il y a aussi une -woption dans grep , mais dans certains cas, cela ne fonctionne pas comme prévu.

De Wikipédia :

cat fruitlist.txt
apple
apples
pineapple
apple-
apple-fruit
fruit-apple

grep -w apple fruitlist.txt
apple
apple-
apple-fruit
fruit-apple
Maciek Sawicki
la source
3

J'ai eu un problème similaire, à la recherche de grep / motif regex et du "motif correspondant trouvé" en sortie.

À la fin, j'ai utilisé egrep (même expression régulière sur grep -e ou -G ne m'a pas donné le même résultat d'egrep) avec l'option -o

donc, je pense que cela pourrait être quelque chose de similaire à (je ne suis pas un maître regex):

egrep -o "the*|this{1}|thoroughly{1}" filename
keebOo
la source
Les {1}quantificateurs inutiles doivent être supprimés. Ou si vous voulez être cohérent, t{1}h{1}e{1}etc.
tripleee
peut-il imprimer avec la même ligne?
吴毅 凡
-1

Vous pouvez diriger votre sortie grep vers Perl comme ceci:

grep "th" * | perl -n -e'while(/(\w*th\w*)/g) {print "$1\n"}'

la source
9
cela ne donnera pas le bon résultat. aussi, si vous utilisez Perl, pas besoin d'utiliser grep. faites tout en Perl.
ghostdog74
Merci d'avoir signalé l'erreur, ghostdog74. Je l'ai changé pour imprimer tous les mots sur la ligne, pas seulement le premier.
comme je l'ai dit, grep n'est pas nécessaire. perl -n -e' while (/ (\ s + th \ w *) / g) {print "$ 1 \ n"} 'file
ghostdog74
7
dépend de vous. je ne fais qu'illustrer un point. Si ce n'est pas nécessaire, ne le faites pas. cet extra "|" vous coûtera un processus de plus.
ghostdog74
1
En Perl 5.10 ou version ultérieure: perl -nE '@a = / (regexp) / ig; dites rejoindre "\ n", @a '
Professeur Photon
-1
$ grep -w

Extrait de la page de manuel grep:

-w: sélectionnez uniquement les lignes contenant des correspondances qui forment des mots entiers. Le test est que la sous-chaîne correspondante doit être soit au début de la ligne, soit précédée d'un caractère constituant autre qu'un mot.

pl1nk
la source
1
Cela imprimera toujours la ligne entière contenant la correspondance. Il contraint la correspondance réelle de façon à ce qu'elle thene corresponde plus, par exemple "ces" ou "baignade".
tripleee
-6

ripgrep

Voici l'exemple utilisant ripgrep:

rg -o "(\w+)?th(\w+)?"

Il correspondra à tous les mots correspondants th.

kenorb
la source