Kann grep nur Wörter anzeigen, die dem Suchmuster entsprechen?

684

Gibt es eine Möglichkeit, grep-Ausgabe von "Wörtern" aus Dateien zu erstellen, die dem Suchausdruck entsprechen?

Wenn ich alle Instanzen von "th" in einer Reihe von Dateien finden möchte, kann ich Folgendes tun:

grep "th" *

aber die Ausgabe wird so etwas wie sein (fett ist von mir);

Eine Textdatei: Die Katze saß auf der Matte  
eine andere Textdatei: der schnelle braune Fuchs  
Noch eine Textdatei: Ich hoffe, das erklärt es gründlich 

Ich möchte, dass es mit derselben Suche ausgegeben wird:

the
the
the
this
thoroughly

Ist das mit grep möglich? Oder mit einer anderen Kombination von Werkzeugen?

Neil Baldwin
quelle
2
Die Lösung von Dan Midwood funktioniert perfekt und verdient die Anerkennung.
Hakish
Gibt es eine Möglichkeit, diese übereinstimmenden Wörter zu drucken, ohne die Zeilen zu ändern? Eher sollte die übereinstimmende Zeichenfolge in derselben Zeile bleiben?
Linguist

Antworten:

953

Versuchen Sie es mit grep -o

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

Bearbeiten: Übereinstimmung mit Phils Kommentar

Aus den Dokumenten :

-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
quelle
9
@ user181548, Die Option grep -o funktioniert nur für GNU grep. Wenn Sie also GNU grep nicht verwenden, funktioniert es möglicherweise nicht für Sie.
Ksinkar
5
@ABB Es hängt davon ab, ob Sie den Namen der übereinstimmenden Datei anzeigen möchten oder nicht. Ich bin nicht sicher, unter welchen Bedingungen es angezeigt wird und unter welchen Bedingungen es angezeigt wird, aber ich weiß, dass bei Verwendung von grep in mehreren Verzeichnissen der vollständige Dateipfad für alle übereinstimmenden Dateien angezeigt wurde, während bei -h nur das angezeigt wurde übereinstimmende Wörter ohne Angabe, um welche Datei es sich handelt. Um der ursprünglichen Frage zu entsprechen, halte ich es unter bestimmten Umständen für notwendig.
LokMac
1
Ich brauchte eine Erklärung, was "\w*th\w*" *bedeutet, also dachte ich, ich würde posten. \wist [_ [: alnum:]], daher entspricht dies grundsätzlich jedem "Wort", das 'th' enthält (da \wkein Leerzeichen enthalten ist). Das * nach dem zitierten Abschnitt ist ein Glob, für den Dateien (dh alle Dateien in diesem Verzeichnis übereinstimmen)
jeremysprofile
1
\wist im Allgemeinen nicht tragbar für grep -E; Verwenden Sie für eine ordnungsgemäße Portabilität [[:alnum:]]stattdessen den Namen der POSIX-Zeichenklasse (oder [_[:alnum:]]wenn Sie den Unterstrich auch wirklich möchten; oder versuchen Sie, grep -Pob Ihre Plattform diesen hat).
Tripleee
@ABB Angesichts der vom OP angezeigten gewünschten Ausgabe ist das absolut -hnotwendig, würde ich sagen ..?
El Ronnoco
80

Cross Distribution sichere Antwort (einschließlich Windows minGW?)

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

Wenn Sie ältere Versionen von grep (wie 2.4.2) verwenden, die die Option -o nicht enthalten. Verwenden Sie die oben genannten. Andernfalls verwenden Sie die einfachere, um die unten stehende Version zu pflegen.

Linux Cross Distribution sichere Antwort

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

Um Zusammenfassungen -ohauszugeben, stimmt der reguläre Ausdruck mit dem Dateiinhalt (und nicht mit dem Dateinamen) überein, genau wie Sie erwarten würden, dass regulärer Ausdruck in vim / etc funktioniert ... Welches Wort oder welcher reguläre Ausdruck Sie dann suchen würden, hängt davon ab Du! Solange Sie bei POSIX und nicht bei der Perl-Syntax bleiben (siehe unten)

Mehr aus dem Handbuch für 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 `[[:>:]]';

Der Grund, warum die ursprüngliche Antwort nicht für alle funktioniert

Die Verwendung von \wvariiert von Plattform zu Plattform, da es sich um eine erweiterte "Perl" -Syntax handelt. Daher verwendet die Grep-Installation, die auf die Arbeit mit POSIX-Zeichenklassen beschränkt ist, [[:alpha:]]nicht das Perl-Äquivalent von \w. Weitere Informationen finden Sie auf der Wikipedia-Seite zum regulären Ausdruck

Letztendlich wird die obige POSIX-Antwort unabhängig von der Plattform (die das Original ist) für grep viel zuverlässiger sein

Für die Unterstützung von grep ohne die Option -o gibt der erste grep die relevanten Zeilen aus, der tr teilt die Leerzeichen in neue Zeilen auf, der letzte grep filtert nur für die jeweiligen Zeilen.

(PS: Ich weiß, dass die meisten Plattformen inzwischen für \ w gepatcht worden wären ... aber es gibt immer solche, die zurückbleiben)

Gutschrift für die "-o" -Umgehung aus der Antwort von @AdamRosenfield

PicoCreator
quelle
1
Was ist mit -o nur in GNU grep arbeiten (wie ksinkar in einem Kommentar zur akzeptierten Antwort erwähnt)?
Brilliand
@Brilliand hmm, ich habe Probleme, eine Linux-Implementierung zu finden, die '-o' nicht unterstützt. Ich kann nach einer Lösung suchen, wenn ich weiß, gegen welche Plattform ich prüfen soll.
PicoCreator
@pico Die -oOption ist in Windows Grep, das mit dem Git-Paket (minGW?) installiert wird, nicht vorhanden: "c:\Program Files (x86)\Git\bin\grep" --version grep (GNU grep) 2.4.2
Bruce Peterson
@BrucePeterson Ich habe in AdamRosenfield eine Problemumgehungsantwort für -o hinzugefügt: Helfen Sie mir zu überprüfen, ob das Windows-Git tr / sed und seine Version enthält. So kann ich überprüfen, ob diese
Problemumgehung
@pico: für GIT: GNU sed Version 4.2.1, tr (GNU textutils) 2.0
Bruce Peterson
45

Es ist einfacher als Sie denken. Versuche dies:

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

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

Wo,

 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
quelle
2
Dies scheint nichts über die bestehenden Antworten von vor mehr als 4 Jahren hinzuzufügen.
Tripleee
3
@tripleee Ich fand meinen Ansatz besser und einfacher, also habe ich dies gepostet.
Abhinandan Prasad
42

Sie können Leerzeichen in Zeilenumbrüche übersetzen und dann grep, z.

cat * | tr ' ' '\n' | grep th
Adam Rosenfield
quelle
18
keine Notwendigkeit Katze. tr '' '\ n' <Datei | grep th. Langsam für große Dateien.
Ghostdog74
Das hat nicht funktioniert. Die Ausgabe enthielt weiterhin den Dateinamen und die gesamte Zeile aus der Datei, die die Übereinstimmung enthielt. Wie auch immer, eine der anderen angebotenen Lösungen hat funktioniert. Vielen Dank für die Eingabe.
Neil Baldwin
@ Ghostdog74: Guter Punkt, obwohl, wenn Sie mehr als Datei haben, müssen Sie Katze verwenden. @ Neil Baldwin: Bist du sicher, dass du es richtig eingegeben hast? Wenn es nur eine Eingabedatei gibt (in diesem Fall stdin), druckt grep den Dateinamen nicht.
Adam Rosenfield
@Adam - ja, sorry Adam, es funktioniert mit einer Datei, aber nicht mit mehreren.
Neil Baldwin
4
@ Ghostdog74 Wenn der langsame Teil wegen ist tr, könnte er grepzuerst tun , trwürde also nur auf übereinstimmende Linien angewendet werden:grep th filename | tr ' ' '\n' | grep th
Carcamano
37

Nur awkkeine Kombination von Werkzeugen erforderlich.

# awk '{for(i=1;i<=NF;i++){if($i~/^th/){print $i}}}' file
the
the
the
this
thoroughly
Ghostdog74
quelle
8
@AjeetGanga gut, es ist im Namen
Daerdemandt
11

grep Befehl nur für Matching und Perl

grep -o -P 'th.*? ' filename
Raghu
quelle
3
Was ist mit der Anzeige nur der übereinstimmenden Gruppe?
Bishwas Mishra
Das funktioniert nicht; Es wird immer nur gefunden, thweil Sie die kürzestmögliche Wiederholung des Platzhalters angefordert haben.
Tripleee
@tripleee - dieses Problem tritt nicht auf, da am Ende der Regex ein Leerzeichen steht. Es fehlen jedoch Wörter, nach denen keine Leerzeichen stehen, z. B. am Ende von Zeilen.
Ken Williams
8

Ich war unzufrieden mit der schwer zu merkenden Syntax von awk, aber ich mochte die Idee, ein einziges Dienstprogramm zu verwenden, um dies zu tun.

Es scheint, als ob ack (oder ack-grep, wenn Sie Ubuntu verwenden) dies leicht tun kann:

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

the
the
the
this
thoroughly

Wenn Sie das Flag -h weglassen, erhalten Sie:

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

some-other-text-file
1:the

some-text-file
1:the
the

yet-another-text-file
1:this
thoroughly

Als Bonus können Sie das --outputFlag verwenden, um dies für komplexere Suchvorgänge mit der einfachsten Syntax zu tun, die ich gefunden habe:

# 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
quelle
8
cat *-text-file | grep -Eio "th[a-z]+"
Mac murmeln
quelle
2
oder einfach grep -Eio "th [az] +" Dateiname
Shayan
3
Vielleicht sehen Sie auch nutzlose Verwendung von cat?
Tripleee
4

Um alle Wörter mit dem Start mit "icon-" zu suchen, funktioniert der folgende Befehl perfekt. Ich verwende hier Ack , das ähnlich wie grep ist, aber bessere Optionen und eine gute Formatierung aufweist.

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

Sie können auch pcregrep ausprobieren . Es gibt auch eine -wOption in grep , aber in einigen Fällen funktioniert sie nicht wie erwartet.

Aus Wikipedia :

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

grep -w apple fruitlist.txt
apple
apple-
apple-fruit
fruit-apple
Maciek Sawicki
quelle
3

Ich hatte ein ähnliches Problem, als ich nach Grep / Pattern Regex und dem "Matched Pattern Found" als Ausgabe suchte.

Am Ende habe ich egrep (der gleiche reguläre Ausdruck auf grep -e oder -G hat mir nicht das gleiche Ergebnis von egrep gegeben) mit der Option -o verwendet

Ich denke, das könnte etwas Ähnliches sein (ich bin KEIN Regex-Meister):

egrep -o "the*|this{1}|thoroughly{1}" filename
keebOo
quelle
Die nutzlosen {1}Quantifizierer sollten fallengelassen werden. Oder wenn Sie konsequent sein wollen, t{1}h{1}e{1}etc.
Tripleee
Kann es mit derselben Zeile drucken?
27 凡
-1

Sie können Ihre grep-Ausgabe folgendermaßen in Perl leiten:

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

quelle
9
das wird nicht das richtige Ergebnis liefern. Wenn Sie Perl verwenden, müssen Sie auch grep nicht verwenden. mach alles in Perl.
Ghostdog74
Vielen Dank für den Hinweis auf den Fehler, ghostdog74. Ich habe es geändert, um alle Wörter in der Zeile zu drucken, nicht nur die ersten.
wie gesagt, grep ist nicht nötig. perl -n -e'while (/ (\ s + th \ w *) / g) {print "$ 1 \ n"} 'Datei
ghostdog74
7
wie du willst. Ich illustriere nur einen Punkt. Wenn es nicht notwendig ist, tu es nicht. das extra "|" kostet Sie einen Prozess mehr.
Ghostdog74
1
In Perl 5.10 oder höher: perl -nE '@a = / (regulärer Ausdruck) / ig; Sagen Sie Join "\ n", @a '
Professor Photon
-1
$ grep -w

Auszug aus der Grep-Manpage:

-w: Wählen Sie nur die Zeilen aus, die Übereinstimmungen enthalten, die ganze Wörter bilden. Der Test besteht darin, dass der übereinstimmende Teilstring entweder am Anfang der Zeile stehen muss oder ein Nicht-Wort-Bestandteil vorangestellt sein muss.

pl1nk
quelle
1
Dadurch wird weiterhin die gesamte Zeile mit der Übereinstimmung gedruckt. Es schränkt die tatsächliche Übereinstimmung so ein, dass thenicht mehr Übereinstimmungen zB "diese" oder "baden" übereinstimmen.
Tripleee
-6

ripgrep

Hier ist das Beispiel mit ripgrep:

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

Es werden alle Wörter übereinstimmen, die übereinstimmen th.

Kenorb
quelle