Czy grep może wyświetlać tylko słowa pasujące do wzorca wyszukiwania?


685

Czy istnieje sposób, aby grep wyprowadzał „słowa” z plików pasujących do wyrażenia wyszukiwania?

Jeśli chcę znaleźć wszystkie wystąpienia np. „Th” w wielu plikach, mogę:

grep "th" *

ale wynik będzie podobny (pogrubiony przeze mnie);

niektóre-text-file: kot siedział na tej maty  
some-other-text-file: Szybki brązowy lis  
jeszcze inny plik tekstowy: mam nadzieję, że to dokładnie to wyjaśnia 

To, co chcę, aby wyświetlało, używając tego samego wyszukiwania, to:

the
the
the
this
thoroughly

Czy jest to możliwe przy użyciu grep? Lub używając innej kombinacji narzędzi?


2
Rozwiązanie Dan Midwood działa idealnie i zasługuje na uznanie.
hakish

Czy istnieje sposób, aby wydrukować te dopasowane słowa bez zmiany linii. Czy dopasowany ciąg powinien pozostać w tej samej linii?
Lingwista

Odpowiedzi:


955

Spróbuj grep -o

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

Edycja: dopasowanie z komentarza Phila

Z dokumentów :

-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, Opcja grep -o działa tylko dla GNU grep. Więc jeśli nie używasz GNU grep, może to nie działać.
ksinkar

5
@ABB Zależy, czy chcesz wyświetlić nazwę dopasowanego pliku, czy nie. Nie jestem pewien, w jakich warunkach to działa i nie wyświetla się, ale wiem, że kiedy użyłem grep w wielu katalogach, wyświetlił pełną ścieżkę do wszystkich pasujących plików, podczas gdy -h po prostu wyświetliło dopasowane słowa bez żadnej specyfikacji, który to plik. Tak więc, aby dopasować oryginalne pytanie, myślę, że jest to konieczne w pewnych okolicznościach.
LokMac,

1
Potrzebowałem wyjaśnienia, co "\w*th\w*" *oznacza, więc pomyślałem, że opublikuję. \wto [_ [: alnum:]], więc w zasadzie pasuje do każdego „słowa” zawierającego „th” (ponieważ \wnie zawiera spacji). * Po cytowanej sekcji jest globem, dla którego pliki (tzn.
Pasują

1
\wzasadniczo nie jest przenośny grep -E; w celu zapewnienia odpowiedniej przenośności użyj [[:alnum:]]zamiast tego nazwy klasy znaków POSIX (lub [_[:alnum:]]jeśli naprawdę chcesz też podkreślenia; lub spróbuj, grep -Pjeśli Twoja platforma to ma).
tripleee

@ABB Biorąc pod uwagę pożądaną wydajność przedstawioną przez PO, -hjest to całkowicie konieczne, powiedziałbym ..?
El Ronnoco

81

Bezpieczna odpowiedź na krzyżową dystrybucję (w tym Windows MinGW?)

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

Jeśli używasz starszych wersji grep (takich jak 2.4.2), które nie zawierają opcji -o. Użyj powyższego. W innym przypadku skorzystaj z prostszej wersji, aby zachować wersję poniżej.

Bezpieczna odpowiedź na dystrybucję w systemie Linux

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

Aby podsumować -ohwyniki, wyrażenie regularne pasuje do zawartości pliku (a nie jego nazwy pliku), podobnie jak oczekiwałbyś, że wyrażenie regularne będzie działać w vim / etc ... To, jakie słowo lub wyrażenie regularne będzie wtedy wyszukiwane, zależy od ty! Tak długo, jak pozostajesz w POSIX, a nie w perlowej składni (patrz poniżej)

Więcej z instrukcji 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 `[[:>:]]';

Powód, dla którego oryginalna odpowiedź nie działa dla wszystkich

Użycie \wróżni się w zależności od platformy, ponieważ jest to rozszerzona składnia „perl”. W związku z tym instalacje grep, które są ograniczone do pracy z klasami znaków POSIX, [[:alpha:]]a nie ich odpowiednikiem w perlu \w. Więcej informacji można znaleźć na stronie Wikipedii dotyczącej wyrażeń regularnych

Ostatecznie powyższa odpowiedź na POSIX będzie dużo bardziej niezawodna niezależnie od platformy (będącej oryginalną) dla grep

Jeśli chodzi o obsługę grep bez opcji -o, pierwszy grep wypisuje odpowiednie linie, tr dzieli spacje na nowe linie, a końcowe filtry grep tylko dla odpowiednich linii.

(PS: Wiem, że do tej pory większość platform zostałaby załatana dla \ w .... ale zawsze są takie, które pozostają w tyle)

Podziękowania za obejście „-o” z odpowiedzi @AdamRosenfield


1
Co powiesz na -o działający tylko w GNU grep (jak ksinkar wspomniał w komentarzu do zaakceptowanej odpowiedzi)?
Brilliand

@Brilliand hmm, mam problem ze znalezieniem implementacji Linuksa, która nie obsługuje „-o”, mogę poszukać obejścia, jeśli wiem, z którą platformą sprawdzić.
PicoCreator,

@pico Ta -oopcja nie jest dostępna w grep systemu Windows, który instaluje się z pakietem git (minGW?): "c:\Program Files (x86)\Git\bin\grep" --version grep (GNU grep) 2.4.2
Bruce Peterson

@BrucePeterson dodałem w obejściu obejścia AdamaRosenfielda dla -o: Pomóż mi sprawdzić, czy git dla systemu Windows zawiera tr / sed i jego wersję. Więc mogę sprawdzić, czy to obejście działa
PicoCreator

@pico: dla GIT: GNU sed wersja 4.2.1, tr (GNU textutils) 2.0
Bruce Peterson

46

To prostsze niż myślisz. Spróbuj tego:

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

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

Gdzie,

 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
Wydaje się, że to nic nie dodaje do istniejących odpowiedzi z ponad 4 lat wcześniej.
tripleee

3
@tripleee Stwierdziłem, że moje podejście jest lepsze i proste, więc opublikowałem to.
Abhinandan prasad

42

Możesz przetłumaczyć spacje na znaki nowej linii, a następnie grep, np .:

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

18
nie ma potrzeby kota. tr '' '\ n' <plik | grep th. Wolno dla dużych plików.
ghostdog74 10.10.2009

To nie zadziałało. Dane wyjściowe nadal zawierały nazwę pliku i cały wiersz z pliku zawierającego dopasowanie. W każdym razie jedno z pozostałych oferowanych rozwiązań działało. W każdym razie dziękuję za wkład.
Neil Baldwin,

@ ghostdog74: dobra uwaga, chociaż jeśli masz więcej niż plik, musisz użyć cat. @Neil Baldwin: czy na pewno wpisałeś to, prawda? Gdy jest tylko jeden plik wejściowy (w tym przypadku stdin), grep nie drukuje nazwy pliku.
Adam Rosenfield,

@Adam - tak, przepraszam Adam, działa z jednym plikiem, ale nie z wieloma.
Neil Baldwin,

4
@ ghostdog74, jeśli powolna część jest z powodu tr, mógł zrobić grepnajpierw, więc trzostanie zastosowana tylko do pasujących linii:grep th filename | tr ' ' '\n' | grep th
Carcamano

37

Po prostu awknie potrzebujesz kombinacji narzędzi.

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

8
@AjeetGanga, no cóż, to nazwa
Daerdemandt,

11

polecenie grep tylko dla dopasowania i perla

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

3
Co z wyświetlaniem tylko dopasowanej grupy?
Bishwas Mishra

To nie działa; znajdzie się tylko thdlatego, że zażądałeś możliwie najkrótszego powtórzenia symbolu wieloznacznego.
tripleee

@tripleee - nie będzie miał tego problemu, ponieważ na końcu wyrażenia jest miejsce. Będzie jednak brakować słów, które nie mają spacji po nich, np. Na końcach wierszy.
Ken Williams

8

Nie byłem zadowolony z trudnej do zapamiętania składni awk, ale podobał mi się pomysł użycia jednego narzędzia do tego celu.

Wygląda na to, że ack (lub ack-grep, jeśli używasz Ubuntu) może to zrobić łatwo:

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

the
the
the
this
thoroughly

Jeśli pominiesz flagę -h, otrzymasz:

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

some-other-text-file
1:the

some-text-file
1:the
the

yet-another-text-file
1:this
thoroughly

Jako bonus możesz użyć --outputflagi, aby zrobić to w przypadku bardziej skomplikowanych wyszukiwań, korzystając z najłatwiejszej składni, jaką znalazłem:

# 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

Aby wyszukać wszystkie słowa, zaczynając od „ikona-”, poniższe polecenie działa idealnie. Używam tutaj Ack , który jest podobny do grep, ale z lepszymi opcjami i ładnym formatowaniem.

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

3

Możesz także wypróbować pcregrep . Istnieje również -wopcja w grep , ale w niektórych przypadkach nie działa zgodnie z oczekiwaniami.

Z Wikipedii :

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

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

3

Miałem podobny problem, szukając wyrażenia regularnego grep / pattern i „dopasowanego wzorca znalezionego” jako wyniku.

Na koniec użyłem egrep (ten sam regex na grep -e lub -G nie dał mi tego samego wyniku egrep) z opcją -o

więc myślę, że może to być coś podobnego (NIE jestem mistrzem wyrażeń regularnych):

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

Bezużyteczne {1}kwantyfikatory powinny zostać porzucone. Lub jeśli chcesz być konsekwentny t{1}h{1}e{1}itp.
tripleee

czy może drukować z tą samą linią?
吴毅 凡

-1

Możesz przesłać dane wyjściowe grep do Perla w następujący sposób:

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

9
to nie da poprawnego wyniku. Ponadto, jeśli używasz Perla, nie musisz używać grep. rób wszystko w Perlu.
ghostdog74 10.10.2009

Dzięki za wskazanie błędu, ghostdog74. Zmieniłem to, aby wydrukować wszystkie słowa w wierszu, a nie tylko pierwsze.

jak powiedziałem, grep nie jest konieczny. perl -n -e'while (/ (\ s + th \ w *) / g) {print "$ 1 \ n"} 'plik
ghostdog74

7
zależy od Ciebie. ja tylko ilustruję punkt. Jeśli nie jest to konieczne, nie rób tego. ten dodatkowy „|” będzie cię kosztować o jeden proces więcej.
ghostdog74,

1
W Perlu 5.10 lub nowszym: perl -nE '@a = / (regexp) / ig; powiedz dołącz do „\ n”, @a ”
Profesor Photon

-1
$ grep -w

Fragment strony man grep:

-w: Wybierz tylko te wiersze zawierające dopasowania, które tworzą całe słowa. Test polega na tym, że pasujący podłańcuch musi znajdować się na początku wiersza lub poprzedzony znakiem składającym się z innych słów.


1
To nadal wypisze całą linię zawierającą dopasowanie. Ogranicza faktyczne dopasowanie, aby thenie pasowało już np. „Te” lub „kąpać się”.
tripleee

-6

ripgrep

Oto przykład z użyciem ripgrep:

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

Dopasuje wszystkie pasujące słowa th.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.