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

Скажем, у меня есть файл:

# file: 'test.txt'
foobar bash 1
удар
foobar happy
Foobar

Я только хочу знать, какие слова появляются после «foobar», поэтому я могу использовать это регулярное выражение:

"foobar \ (\ w \ + \)"

В скобках указывается, что у меня есть особый интерес к слову сразу после foobar. Но когда я делаю grep "foobar \ (\ w \ + \)" test.txt, я получаю все строки, которые соответствуют всему регулярному выражению, а не просто "слово после foobar":

foobar bash 1
foobar happy

Я бы предпочел, чтобы результат этой команды выглядел так:

Баш
счастливый

Есть ли способ показать grep только вывод элементов, которые соответствуют группировке (или определенной группировке) в регулярном выражении?

203 голоса | спросил Cory Klein 20 Mayam11 2011, 03:04:02

7 ответов


244

GNU grep имеет параметр -P для регулярных выражений в стиле perl и параметр -o для печати только того, что соответствует шаблону. Их можно объединить, используя утверждения look-around (описанные в расширенные шаблоны в man-странице perlre ), чтобы удалить часть шаблона grep из того, что определено для соответствия -o.

$ grep -oP 'foobar \ K \ w +' test.txt
удар
счастливый
$

\ K - это короткая форма (и более эффективная форма) (? <= pattern), которую вы используете в качестве нулевой ширины утверждение перед текстом, который вы хотите вывести. (? = pattern) может использоваться как утверждение с ожиданием нулевой ширины после текста, который вы хотите вывести.

Например, если вы хотите совместить слово между foo и bar, вы можете использовать:

$ grep -oP 'foo \ K \ w + (? = bar)' test.txt

или (для симметрии)

$ grep -oP '(? <= foo) \ w + (? = bar)' test.txt
ответил camh 20 Mayam11 2011, 05:33:27
25

Стандартный grep не может этого сделать, но последние версии GNU grep . Вы можете обратиться к sed, awk или perl. Вот несколько примеров, которые делают то, что вы хотите на своем примере ввода; они ведут себя несколько иначе в угловых случаях.

Заменить foobar word other stuff на word, распечатать, только если замена выполнена.

sed -n -e 's /^ foobar \ ([[: alnum:]] \ + \). * /\ 1 /p'

Если первое слово foobar, напечатайте второе слово.

awk '$ 1 == "foobar" {print $ 2}'

Strip foobar, если это первое слово, и пропустите строку иначе; затем разделите все после первого пробела и распечатайте.

perl -lne 's /^ foobar \ s + //или next; s /\ s * //. Распечатать'
ответил Gilles 20 Mayam11 2011, 03:17:40
14
sed -n "s /^.* foobar \ s * \ (\ S * \). * $ /\ 1 /p"

-n подавлять печать
s заменить
^. * ничего до foobar
сопоставление исходного поиска foobar
\ s * любой символ пробела (пробел)
\ (начало группы захвата
\ S * захватить любой небелый пробел (слово)
\) конечная группа захвата
. * $ ничего после группы захвата
\ 1 заменить все на первую группу захвата
p распечатать
ответил jgshawkey 22 PMpFri, 22 Apr 2016 19:08:37 +030008Friday 2016, 19:08:37
13

Хорошо, если вы знаете, что foobar всегда является первым словом или строкой, то вы можете использовать cut. Например:

grep "foobar" test.file | cut -d "" -f2
ответил Dave 20 Mayam11 2011, 05:07:12
7

Если PCRE не поддерживается, вы можете получить тот же результат с двумя вызовами grep. Например, чтобы захватить слово после foobar сделать это:

<test.txt grep -o 'foobar * [^] *' | grep -o '[^] * $'

Это можно развернуть до произвольного слова после foobar , как это (с ERE для чтения):

я = 1
<test.txt egrep -o 'foobar + ([^] + +) {' $ i '} [^] +' | grep -o '[^] * $'

Вывод:

1

Обратите внимание, что индекс i основан на нуле.

ответил Thor 8 +04002013-10-08T16:38:10+04:00312013bEurope/MoscowTue, 08 Oct 2013 16:38:10 +0400 2013, 16:38:10
1

pcregrep имеет более умный -o вариант который позволяет вам выбирать, какие группы захвата вы хотите выводить. Итак, используя ваш примерный файл,

$ pcregrep -o1 "foobar (\ w +)" test.txt
удар
счастливый
ответил G-Man 14 AMpSat, 14 Apr 2018 10:29:46 +030029Saturday 2018, 10:29:46
0

Использование grep не совместимо с кросс-платформой, так как -P /- perl-regexp доступно только на GNU grep , а не BSD grep .

Вот решение, использующее ripgrep :

$ rg -o "foobar (\ w +)" -r '$ 1' <test.txt
удар
счастливый

В соответствии с man rg:

  

-r /- заменить REPLACEMENT_TEXT Заменить каждое соответствие заданным текстом.

     

Индексы группы захвата (например, $ 5) и имена (например, $ foo) поддерживаются в заменяющей строке.

Связанный: GH-462 .

ответил kenorb 16 PMpMon, 16 Apr 2018 18:35:18 +030035Monday 2018, 18:35:18

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132