Может ли grep показывать только те слова, которые соответствуют шаблону поиска?

685

Есть ли способ заставить grep выводить «слова» из файлов, которые соответствуют поисковому выражению?

Если я хочу найти все экземпляры, скажем, «th» в нескольких файлах, я могу сделать:

grep "th" *

но на выходе будет что-то вроде (жирным шрифтом - я);

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

То, что я хочу вывести, используя тот же поиск, это:

the
the
the
this
thoroughly

Возможно ли это с помощью grep? Или используя другую комбинацию инструментов?

Нил Болдуин
источник
2
Решение Dan Midwood отлично работает и заслуживает похвалы.
Хакиш
Есть ли способ, которым можно напечатать эти совпадающие слова без изменения строк. Скорее совпадающая строка должна остаться в той же строке?
лингвист

Ответы:

958

Попробуй 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*" *значит, поэтому я решил опубликовать. \wis [_ [: alnum:]], так что это соответствует практически любому «слову», которое содержит «th» (так \wкак не включает пробел). Знак * после цитируемого раздела - это глобус, для которого файлы (то есть совпадающие со всеми файлами в этом каталоге)
jeremysprofile
1
\wобычно не переносим grep -E; для правильной переносимости используйте [[:alnum:]]вместо этого имя класса символов POSIX (или, [_[:alnum:]]если вы действительно хотите, чтобы подчеркивание тоже; или попробуйте, grep -Pесли ваша платформа имеет это).
tripleee
@ABB Учитывая желаемый результат, показанный OP, -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 / etc ... Какое слово или регулярное выражение вы бы искали тогда, до вы! Пока вы остаетесь в 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

PicoCreator
источник
1
Как насчет -o, работающего только в GNU grep (как упомянуто в комментарии к принятому ответу ksinkar)?
Brilliand
@ Brilliand Хмм, у меня проблемы с поиском реализации linux, которая не поддерживает '-o', я могу искать обходной путь, если знаю, какую платформу проверять.
PicoCreator
@pico -oОпция отсутствует в Windows grep, который устанавливается вместе с пакетом git (minGW?): "c:\Program Files (x86)\Git\bin\grep" --version grep (GNU grep) 2.4.2
Брюс Петерсон
@BrucePeterson Я добавил в обходной путь AdamRosenfield ответ для -o: Помогите мне проверить, включает ли windows git 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+ года до этого.
tripleee
3
@tripleee Я нашел, что мой подход лучше и проще, поэтому я опубликовал это.
Абхинандан Прасад
42

Вы можете перевести пробелы в новую строку, а затем grep, например:

cat * | tr ' ' '\n' | grep th
Адам Розенфилд
источник
18
нет необходимости кот. tr '' '\ n' <файл | grep th. Медленно для больших файлов.
ghostdog74
Это не сработало. Вывод все еще содержал имя файла и всю строку из файла, который содержал совпадение. В любом случае, одно из предложенных решений сработало. Спасибо за вклад, хотя.
Нил Болдуин
@ ghostdog74: хорошая мысль, хотя если у вас больше файла, вам нужно использовать cat. @ Нил Болдуин: ты уверен, что набрал его правильно? Когда есть только один входной файл (в данном случае 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
ghostdog74
источник
8
@AjeetGanga хорошо, это во имя
Daerdemandt
11

команда grep только для сопоставления и perl

grep -o -P 'th.*? ' filename
Рагу
источник
3
Как насчет отображения только соответствующей группы?
Бишвис Мишра
Это не работает; он будет найден только thпотому, что вы запросили кратчайшее повторение подстановочного знака.
tripleee
@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
Sandeep
источник
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
Maciek Sawicki
источник
3

У меня была похожая проблема, когда я искал grep / pattern regex и «соответствовал шаблон найден» в качестве вывода.

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

Итак, я думаю, что это может быть что-то похожее (я НЕ мастер регулярных выражений):

egrep -o "the*|this{1}|thoroughly{1}" filename
keebOo
источник
Бесполезные {1}квантификаторы должны быть отброшены. Или если вы хотите быть последовательным и t{1}h{1}e{1}т. Д.
tripleee
он может печатать с той же линией?
凡 凡
-1

Вы можете перенаправить вывод grep в Perl следующим образом:

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

источник
9
это не даст правильный результат. также, если вы используете Perl, нет необходимости использовать grep. делать все на Perl.
ghostdog74
Спасибо за указание на ошибку, ghostdog74. Я изменил его, чтобы печатать все слова в строке, а не только первое.
Как я уже сказал, grep не нужен. файл perl -n -e' while (/ (\ 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:

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

pl1nk
источник
1
Это все равно напечатает всю строку, содержащую совпадение. Это ограничивает фактическое совпадение так, чтобы theбольше не совпадало, например, с "этими" или "купаться".
tripleee
-6

ripgrep

Вот пример использования ripgrep:

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

Это будет соответствовать всем словам соответствия th.

kenorb
источник