Может ли 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 только вывод элементов, которые соответствуют группировке (или определенной группировке) в регулярном выражении?
7 ответов
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
Стандартный 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 * //. Распечатать'
sed -n "s /^.* foobar \ s * \ (\ S * \). * $ /\ 1 /p"
-n подавлять печать
s заменить
^. * ничего до foobar
сопоставление исходного поиска foobar
\ s * любой символ пробела (пробел)
\ (начало группы захвата
\ S * захватить любой небелый пробел (слово)
\) конечная группа захвата
. * $ ничего после группы захвата
\ 1 заменить все на первую группу захвата
p распечатать
Хорошо, если вы знаете, что foobar всегда является первым словом или строкой, то вы можете использовать cut. Например:
grep "foobar" test.file | cut -d "" -f2
Если PCRE не поддерживается, вы можете получить тот же результат с двумя вызовами grep. Например, чтобы захватить слово после foobar сделать это:
<test.txt grep -o 'foobar * [^] *' | grep -o '[^] * $'
Это можно развернуть до произвольного слова после foobar , как это (с ERE для чтения):
я = 1
<test.txt egrep -o 'foobar + ([^] + +) {' $ i '} [^] +' | grep -o '[^] * $'
Вывод:
1
Обратите внимание, что индекс i
основан на нуле.
pcregrep
имеет более умный -o
вариант
который позволяет вам выбирать, какие группы захвата вы хотите выводить.
Итак, используя ваш примерный файл,
$ pcregrep -o1 "foobar (\ w +)" test.txt
удар
счастливый
Использование 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 .