Grep può mostrare solo parole che corrispondono al modello di ricerca?


685

Esiste un modo per creare "parole" in grep da file che corrispondono all'espressione di ricerca?

Se voglio trovare tutte le istanze di, diciamo, "th" in un numero di file, posso fare:

grep "th" *

ma l'output sarà simile a (grassetto è da parte mia);

qualche-file di testo: il gatto seduto sul il tappeto  
qualche altro file di testo: la veloce volpe marrone  
ancora un altro file di testo: spero che questo lo spieghi a fondo 

Quello che voglio che emetta, usando la stessa ricerca, è:

the
the
the
this
thoroughly

È possibile usare grep? O usando un'altra combinazione di strumenti?


2
La soluzione Dan Midwood funziona perfettamente e merita il merito.
hakish,

C'è un modo in cui è possibile stampare quelle parole abbinate senza cambiare le righe. Piuttosto la stringa abbinata dovrebbe rimanere nella stessa riga?
Linguista,

Risposte:


958

Prova grep -o

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

Modifica: corrispondenza dal commento di Phil

Dai documenti :

-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.

9
@ user181548, L'opzione grep -o funziona solo per grep GNU. Quindi se non stai usando GNU grep, potrebbe non funzionare per te.
ksinkar,

5
@ABB Dipende se si desidera visualizzare il nome del file corrispondente o meno. Non sono sicuro in quali condizioni sia e non venga visualizzato, ma so che quando ho usato grep in un certo numero di directory mostrava il percorso completo del file per tutti i file corrispondenti, mentre con -h mostrava solo il parole corrispondenti senza alcuna specifica su quale file sia. Quindi, per abbinare la domanda originale, penso che sia necessario in determinate circostanze.
LokMac,

1
Avevo bisogno di una spiegazione per cosa "\w*th\w*" *significhi, quindi ho pensato di postare. \wè [_ [: alnum:]], quindi corrisponde sostanzialmente a qualsiasi "parola" che contiene "th" (poiché \wnon include lo spazio). Il * dopo la sezione tra virgolette è un glob per cui i file (ovvero, che corrispondono a tutti i file in questa directory)
jeremysprofile

1
\wgeneralmente non è portatile grep -E; per una portabilità corretta, usa [[:alnum:]]invece il nome della classe di caratteri POSIX (o [_[:alnum:]]se vuoi davvero anche il trattino basso; oppure prova grep -Pse la tua piattaforma lo possiede).
triplo il

@ABB Dato l'output desiderato mostrato dall'OP -hè del tutto necessario direi ..?
El Ronnoco,

81

Risposta sicura per la distribuzione incrociata (incluso Windows minGW?)

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

Se stai usando versioni precedenti di grep (come 2.4.2) che non include l'opzione -o. Usa quanto sopra. Altrimenti usa il più semplice per mantenere la versione di seguito.

Risposta sicura di Linux cross-distribution

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

In sintesi, -ohl'espressione regolare corrisponde al contenuto del file (e non al suo nome file), proprio come ti aspetteresti che l'espressione regolare funzioni in vim / etc ... Quale parola o espressione regolare dovresti cercare, dipende da voi! Finché rimani in POSIX e non nella sintassi perl (vedi sotto)

Altro dal manuale di 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 `[[:>:]]';

Il motivo per cui la risposta originale non funziona per tutti

L'uso di \wvaria da piattaforma a piattaforma, in quanto è una sintassi estesa "perl". Come tale, quell'installazione grep che è limitata a funzionare con le classi di caratteri POSIX usa [[:alpha:]]e non il suo equivalente perl di \w. Vedi la pagina di Wikipedia sull'espressione regolare per ulteriori informazioni

Alla fine, la risposta POSIX sopra sarà molto più affidabile indipendentemente dalla piattaforma (essendo l'originale) per grep

Per quanto riguarda il supporto di grep senza l'opzione -o, il primo grep produce le linee pertinenti, il tr suddivide gli spazi in nuove linee, i filtri grep finali solo per le rispettive linee.

(PS: ormai conosco la maggior parte delle piattaforme, sarebbe stata patchata per \ w .... ma ci sono sempre quelle che rimangono indietro)

Ringraziamo la soluzione "-o" dalla risposta di @AdamRosenfield


1
Che dire di -o funziona solo in GNU grep (come menzionato da ksinkar in un commento sulla risposta accettata)?
Brilliand,

@Brilliand hmm, sto avendo problemi a trovare un'implementazione di Linux che non supporta '-o', posso cercare un aggancio se conosco la piattaforma da controllare.
PicoCreator,

@pico L' -oopzione non è presente nel grep di Windows che si installa con il pacchetto git (minGW?): "c:\Program Files (x86)\Git\bin\grep" --version grep (GNU grep) 2.4.2
Bruce Peterson

@BrucePeterson ho aggiunto in AdamRosenfield la soluzione alternativa per -o: aiutami a verificare se Windows git include tr / sed e la sua versione. Quindi posso verificare se questa soluzione alternativa funziona
PicoCreator il

@pico: per GIT: GNU sed versione 4.2.1, tr (GNU textutils) 2.0
Bruce Peterson,

46

È più semplice di quanto pensi. Prova questo:

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

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

Dove,

 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.

2
Ciò non sembra aggiungere nulla rispetto alle risposte esistenti di oltre 4 anni prima.
Tripleee

3
@tripleee Ho trovato il mio approccio migliore e semplice, quindi l'ho pubblicato.
Abhinandan prasad,

42

Puoi tradurre gli spazi in newline e quindi grep, ad esempio:

cat * | tr ' ' '\n' | grep th

18
non c'è bisogno di un gatto. tr '' '\ n' <file | grep th. Lento per file di grandi dimensioni.
ghostdog74,

Questo non ha funzionato. L'output conteneva ancora il nome file e l'intera riga del file che conteneva la corrispondenza. Comunque, una delle altre soluzioni offerte ha funzionato. Grazie per l'input però.
Neil Baldwin,

@ ghostdog74: buon punto, anche se se hai più di un file, dovrai usare cat. @Neil Baldwin: sei sicuro di averlo digitato bene? Quando c'è solo un file di input (stdin in questo caso), grep non stampa il nome del file.
Adam Rosenfield

@Adam - sì, scusa Adam, funziona con un file ma non con più file.
Neil Baldwin,

4
@ ghostdog74 se la parte lenta è a causa di tr, potrebbe fare per grepprimo, quindi trverrebbe applicato solo alle linee corrispondenti:grep th filename | tr ' ' '\n' | grep th
Carcamano

37

Solo awk, non è necessaria alcuna combinazione di strumenti.

# awk '{for(i=1;i<=NF;i++){if($i~/^th/){print $i}}}' file
the
the
the
this
thoroughly

8
@AjeetGanga bene, è nel nome
Daerdemandt l'

11

comando grep per solo matching e perl

grep -o -P 'th.*? ' filename

3
Che dire della visualizzazione del solo gruppo abbinato?
Bishwas Mishra il

Questo non funziona; lo troverà solo thperché hai richiesto la ripetizione più breve possibile del carattere jolly.
triplo il

@tripleee - non avrà questo problema, perché c'è uno spazio incluso alla fine della regex. Tuttavia, mancheranno le parole che non hanno spazi dopo di loro, ad esempio alle estremità delle linee.
Ken Williams,

8

Non ero soddisfatto della sintassi di Awk difficile da ricordare, ma mi piaceva l'idea di usare una utility per farlo.

Sembra che ack (o ack-grep se usi Ubuntu) possa farlo facilmente:

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

the
the
the
this
thoroughly

Se si omette il flag -h si ottiene:

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

some-other-text-file
1:the

some-text-file
1:the
the

yet-another-text-file
1:this
thoroughly

Come bonus, puoi usare la --outputbandiera per fare questo per ricerche più complesse con la sintassi più semplice che ho trovato:

# 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


4

Per cercare tutte le parole con inizio con "icona-" il seguente comando funziona perfettamente. Sto usando Ack qui che è simile a grep ma con opzioni migliori e una buona formattazione.

ack -oh --type=html "\w*icon-\w*" | sort | uniq

3

Puoi anche provare pcregrep . C'è anche -wun'opzione in grep , ma in alcuni casi non funziona come previsto.

Da Wikipedia :

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

grep -w apple fruitlist.txt
apple
apple-
apple-fruit
fruit-apple

3

Ho avuto un problema simile, cercando grege / pattern regex e il "pattern trovato trovato" come output.

Alla fine ho usato egrep (lo stesso regex su grep -e o -G non mi ha dato lo stesso risultato di egrep) con l'opzione -o

quindi, penso che potrebbe essere qualcosa di simile a (NON sono un regex Master):

egrep -o "the*|this{1}|thoroughly{1}" filename

I {1}quantificatori inutili dovrebbero essere eliminati. O se vuoi essere coerente, t{1}h{1}e{1}ecc.
tripleee

può stampare con la stessa linea?
吴毅凡

-1

È possibile reindirizzare l'output di grep in Perl in questo modo:

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

9
quello non darà il risultato corretto. inoltre, se si utilizza Perl, non è necessario utilizzare grep. fare tutto in Perl.
ghostdog74,

Grazie per aver segnalato l'errore, ghostdog74. L'ho modificato per stampare tutte le parole sulla riga, non solo le prime.

come ho detto, grep non è necessario. perl -n -e'while (/ (\ s + th \ w *) / g) {print "$ 1 \ n"} 'file
ghostdog74

7
sta a te. sto solo illustrando un punto. Se non è necessario, non farlo. quel extra "|" ti costerà un processo in più.
ghostdog74,

1
In Perl 5.10 o successivo: perl -nE '@a = / (regexp) / ig; dì unisciti a "\ n", @a '
Professor Photon,

-1
$ grep -w

Estratto dalla pagina man di grep:

-w: seleziona solo quelle righe che contengono corrispondenze che formano parole intere. Il test è che la sottostringa corrispondente deve essere all'inizio della riga o preceduta da un carattere costituente non di parole.


1
In questo modo verrà comunque stampata l'intera riga contenente la corrispondenza. Limita la corrispondenza effettiva in modo che thenon corrisponda più ad esempio "questi" o "fare il bagno".
triplo

-6

ripgrep

Ecco l'esempio usando ripgrep:

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

Abbinerà tutte le parole corrispondenti th.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.