Чи може греп показувати лише слова, які відповідають шаблону пошуку?


685

Чи є спосіб зробити греп виведення "слів" з файлів, які відповідають пошуковому виразу?

Якщо я хочу знайти всі екземпляри, скажімо, "th" у ряді файлів, я можу зробити:

grep "th" *

але вихід буде чимось на зразок (сміливий - це я);

деякі-текст-файл: кіт сидів на за мат  
деякі-другий-текстовий файл: спритна лисиця  
ще-інший-текстовий файл: сподіваюсь, це це докладно пояснює 

Я хочу, щоб він виводив, використовуючи той самий пошук, це:

the
the
the
this
thoroughly

Чи можливо це за допомогою grep? Або використовуєте іншу комбінацію інструментів?


2
Рішення Ден Мідвуд прекрасно працює і заслуговує на заслугу.
гакіш

Чи є спосіб вивести ці відповідні слова, не змінюючи рядки. Швидше, щоб відповідна рядок повинна залишатися в одному рядку?
Лінгвіст

Відповіді:


956

Спробуйте grep -o

grep -oh "\w*th\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, опція grep -o працює лише для GNU grep. Тож якщо ви не використовуєте GNU grep, це може не працювати для вас.
ksinkar

5
@ABB Це залежить від того, ви хочете відобразити ім'я відповідного файлу чи ні. Я не впевнений, за яких умов це робиться, а не відображається, але я знаю, що коли я використовував grep у ряді каталогів, він відображав повний шлях до файлу для всіх збігаються файлів, тоді як при -h він просто відображав відповідні слова без будь-якої специфікації, про який файл це. Отже, щоб відповідати початковому питанню, я вважаю, що це необхідно за певних обставин.
LokMac

1
Мені потрібно було пояснення, що "\w*th\w*" *означає, тому я зрозумів, що опублікую. \wє [_ [: alnum:]], тому це в основному відповідає будь-якому "слову", що містить "th" (оскільки \wне включає пробіл). Розділ * після котируваного розділу - це глобус, для якого файли (тобто відповідність усім файлам у цьому каталозі)
jeremysprofile

1
\wяк правило, не переноситься на grep -E; для належної переносимості скористайтеся назвою класу символів POSIX [[:alnum:]](або, [_[:alnum:]]якщо ви теж хочете підкреслити; або спробуйте, grep -Pчи має ваша платформа).
трійка

@ABB Враховуючи бажаний вихід, показаний ОП, -hя цілком необхідний, я б сказав ..?
Ель Ронноко

81

Перехресна безпечна відповідь (включаючи windows minGW?)

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

Якщо ви використовуєте старіші версії grep (наприклад, 2.4.2), які не містять опцію -o. Скористайтеся описаним вище. Інше скористайтеся простішою для підтримки версії нижче.

Безпечна відповідь на перехресний розподіл Linux

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

Для підсумків -ohвиводиться відповідність регулярного вираження вмісту файлу (а не його імені файлу), як і те, як ви б очікували, що регулярний вираз буде працювати у vim / тощо ... Яке слово чи регулярний вираз ви б шукали тоді ви! Поки ви залишаєтесь POSIX, а не синтаксисом perl (див. Нижче)

Більше з посібника з 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 `[[:>:]]';

Причина, чому оригінальна відповідь працює не для всіх

Використання \wзалежить від платформи до платформи, оскільки це розширений синтаксис "perl". Таким чином, використовується програма Grep, яка обмежена роботою з класами символів POSIX, [[:alpha:]]а не її еквівалент perl \w. Додаткову інформацію див. На сторінці Вікіпедії щодо регулярного вираження

Зрештою, відповідь POSIX вище буде набагато надійнішою незалежно від платформи (будучи оригіналом) для grep

Що стосується підтримки grep без опції -o, перший grep виводить відповідні рядки, tr розбиває пробіли на нові рядки, остаточний grep фільтрує лише для відповідних рядків.

(PS: Я знаю, що більшість платформ до цього часу були б виправлені \ w .... але завжди є такі, які відстають)

Заслуга за вирішення "-o" з відповіді @AdamRosenfield


1
А як -о працювати лише в GNU grep (як ksinkar згаданий у коментарі до прийнятої відповіді)?
Brilliand

@Brilliand Хм, у мене виникають проблеми з пошуком реалізації Linux, яка не підтримує '-o', я можу шукати, якщо я знаю, на якій платформі потрібно перевірити.
PicoCreator

@pico -oОпція відсутня в грепі Windows, який встановлюється разом із пакетом git (minGW?): "c:\Program Files (x86)\Git\bin\grep" --version grep (GNU grep) 2.4.2
Брюс Петерсон,

@BrucePeterson Я додав у вирішенні відповіді AdamRosenfield за -o: Допоможіть мені перевірити, чи містить git Windows tr / sed та його версія. Тож я можу перевірити, чи працює це рішення
PicoCreator

@pico: для GIT: GNU sed версія 4.2.1, tr (GNU textutils) 2.0
Брюс Петерсон

46

Це простіше, ніж ти думаєш. Спробуйте це:

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

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

Де,

 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
Це, здається, не додає нічого до існуючих відповідей за останні 4 роки.
трійка

3
@tripleee Я знайшов свій підхід кращим і простим, тому я опублікував це.
Абхіндандан Прасад

42

Ви можете перевести пробіли в нові рядки, а потім простукувати, наприклад:

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

18
не потрібна кішка. tr '' '\ n' <файл | греп ю. Повільно для великих файлів.
ghostdog74

Це не спрацювало. Висновок все ще містив ім'я файлу та весь рядок із файлу, який містив збіг. У будь-якому випадку, одне з інших запропонованих рішень спрацювало. Дякую за вклад, хоча.
Ніл Болдуін

@ ghostdog74: хороший момент, хоча якщо у вас є більше файлу, вам потрібно буде використовувати cat. @Neil Baldwin: ви впевнені, що ввели це правильно? Якщо є лише один вхідний файл (stdin у цьому випадку), grep не друкує ім'я файлу.
Адам Розенфілд

@Adam - так, вибачте Адаме, він працює з одним файлом, але не з декількома.
Ніл Болдуін

4
@ ghostdog74, якщо повільна частина через це tr, він міг би зробити grepперше, тому trзастосовуватиметься лише до відповідних ліній:grep th filename | tr ' ' '\n' | grep th
Carcamano

37

Просто awk, не потрібна комбінація інструментів.

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

8
@AjeetGanga добре, це в назві
Daerdemandt

11

команда grep для лише узгодження та perl

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

3
А що з відображенням лише відповідної групи?
Bishwas Mishra

Це не працює; він знайдеться лише коли-небудь, thоскільки ви просили якомога коротше повторити підстановку.
трійка

@tripleee - це не матиме такої проблеми, оскільки в кінці регулярного виразу є пробіл. Однак він пропустить слова, які не мають пробілів після них, наприклад, на кінцях рядків.
Кен Вільямс

8

Я був незадоволений важким запам'ятовуючим синтаксисом awk, але мені сподобалась ідея використання однієї утиліти для цього.

Здається, що ack (або ack-grep, якщо ви використовуєте Ubuntu), це легко зробити:

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

the
the
the
this
thoroughly

Якщо опустити прапор -h, ви отримаєте:

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

some-other-text-file
1:the

some-text-file
1:the
the

yet-another-text-file
1:this
thoroughly

Як бонус, ви можете використовувати --outputпрапор, щоб зробити це для більш складних пошукових запитів приблизно з найпростішим синтаксисом, який я знайшов:

# 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

Для пошуку всіх слів, починаючи з "icon-", ідеально працює наступна команда. Я тут використовую Ack, який схожий на grep, але з кращими параметрами та приємним форматуванням.

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

3

Ви також можете спробувати pcregrep . Також є -wваріант grep , але в деяких випадках він не працює так, як очікувалося.

З Вікіпедії :

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

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

3

У мене була аналогічна проблема, шукаючи регекс grep / pattern та "знайдений узгоджений шаблон" як вихід.

Наприкінці я використав egrep (той же регулярний вираз на grep -e або -G не дав мені однакового результату egrep) з опцією -o

тому я думаю, що це може бути щось подібне до (я НЕ майстер з регулярних виразів):

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

Даремні {1}квантори повинні бути відкинуті. Або якщо ви хочете бути послідовними і t{1}h{1}e{1}т. Д.
трійка

чи можна друкувати тією ж лінією?
吴毅 凡

-1

Ви можете передати греп-вихід у Perl так:

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

9
це не дасть правильного результату. також, якщо ви використовуєте Perl, не потрібно використовувати grep. робити все в Perl.
ghostdog74

Дякуємо, що вказали на помилку, ghostdog74. Я змінив це, щоб надрукувати всі слова на рядку, не тільки перше.

як я сказав, греп не потрібен. perl -n -e'time (/ (\ s + th \ w *) / g) {print "$ 1 \ n"} 'файл
ghostdog74

7
до вас. я просто ілюструю точку. Якщо це не потрібно, не робіть цього. що додатково "|" обійдеться вам на один процес дорожче.
ghostdog74

1
У Perl 5.10 або новіших версій: perl -nE '@a = / (regexp) / ig; скажіть, приєднайтесь до "\ n", @a '
професор Фотон

-1
$ grep -w

Уривок зі сторінки grep man:

-w: Виберіть лише ті рядки, що містять відповідники, які утворюють цілі слова. Тест полягає в тому, що відповідна підрядка повинна бути або на початку рядка, або перед нею складовим символом без слова.


1
Це все одно буде надрукувати весь рядок, що містить відповідність. Це обмежує фактичну відповідність так, що theбільше не збігається, наприклад, "ці" або "купаються".
трійчатка

-6

ripgrep

Ось приклад із використанням ripgrep:

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

Він буде відповідати всім словам, які відповідають th.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.