Как сделать не жадные совпадения в grep?

Ответы:

276

Вы ищете не жадный (или ленивый) матч. Чтобы получить не жадное совпадение в регулярных выражениях, вам нужно использовать модификатор ?после квантификатора. Например, вы можете изменить .*на .*?.

По умолчанию grepне поддерживает не жадные модификаторы, но вы можете использовать grep -Pдля использования синтаксиса Perl.

Марк Байерс
источник
3
Модификатор eegg: dot all также называется многострочным. Это модификатор, который меняет "." поведение соответствия, чтобы включить новые строки (обычно это не так). В grep такого модификатора нет, но в pcregrep есть .
А. Уилсон
1
Исправление: В большинстве разновидностей регулярных выражений, которые его поддерживают, режим, который позволяет .сопоставлять символы новой строки, называется режимом DOTALL или однострочным ; Ruby - единственный, кто называет это многострочным . В других вариантах многострочный - это режим, который позволяет якорям ( ^и $) совпадать на границах линий. Ruby не имеет эквивалентного режима, потому что в Ruby они всегда работают таким образом.
Алан Мур
5
-Pбыл совершенно новым для меня, я радостно трепал много лет, и только используя -E... так много потраченных лет! - Примечание для себя: перечитайте страницы справочника как обычную вещь, и вы никогда не перевариваете достаточно переключателей и опций.
ocodo
29
На некоторых платформах (например, Mac OS X) grepне поддерживает -P, но если вы используете, egrepвы можете использовать .*?шаблон для достижения того же результата. egrep -o 'start.*?end' text.html
SaltyNuts
4
В качестве дополнения к комментарию @SaltyNuts, Mac OS X не поддерживает, -Pно -Eбудет вызывать, egrepследовательно, предлагаемая работа .*?работает отлично.
Фредрик Эрландссон
83

На самом деле .*?только работает в perl. Я не уверен, каков будет эквивалентный синтаксис расширенного регулярного выражения grep. К счастью, вы можете использовать синтаксис perl с grep, так grep -Pчто это будет работать, но grep -Eэто то же самое, egrepчто не будет работать (это было бы жадно).

Смотрите также: http://blog.vinceliu.com/2008/02/non-greedy-regular-expression-matching.html

Джон Смит
источник
9
grep -Pне работает в GNU grep 2.9 - только что попробовал (это не ошибка, просто молча не применяет ?. Intertestly и не делает класс, например:env|grep '[^\=]*\='
roberto tomás
2
В Darwin / OS X 10.8 Mountain Lion нет grep -Pопций или pgrepкоманд, но egrepотлично работает.
Стив ХХХ,
2
В pgrepмоем окне OS X 10.9 есть команда, но это совершенно другая программа, цель которой - «найти или сообщить о процессах по имени».
Desty
@ robertotomás Отвечая на 6-летний комментарий здесь, но .... Я тоже так подумал, а потом понял, что получаю несколько не жадных совпадений. Например, на цветном терминале вы можете увидеть, что `echo" bbbbb "| grep -P 'b. *? b'` возвращает 2 совпадения.
zzxyz
12

Мой grep, который работает после попытки вещи в этой теме:

echo "hi how are you " | grep -shoP ".*? "

Просто убедитесь, что вы добавляете пробел к каждой из ваших строк

(Мой был построчным поиском, чтобы выплевывать слова)

jonz
источник
3
-shoPхорошая мнемоника :)
Mariusz
echo "bbbbb" | grep -shoP 'b.*?b'немного опыта обучения. Единственная вещь, которая работала для меня с точки зрения явно ленивый, а также.
zzxyz
12

grep

Для не жадных совпадений grepвы можете использовать отрицательный класс символов. Другими словами, старайтесь избегать подстановочных знаков.

Например, чтобы извлечь все ссылки на файлы JPEG из содержимого страницы, вы должны использовать:

grep -o '"[^" ]\+.jpg"'

Для работы с несколькими строками xargsсначала проложите ввод . Для производительности используйте ripgrep.

kenorb
источник
3

Краткий ответ использует следующее регулярное выражение:

(?s)<car .*? model=BMW .*?>.*?</car>
  • (? s) - это делает совпадение по многострочному
  • . *? - соответствует любому персонажу несколько раз ленивым способом (минимальное совпадение)

(Немного) более сложный ответ:

(?s)<([a-z\-_0-9]+?) .*? model=BMW .*?>.*?</\1>

Это позволит сопоставить car1 и car2 в следующем тексте

<car1 ... model=BMW ...>
...
...
...
</car1>
<car2 ... model=BMW ...>
...
...
...
</car2>
  • (..) представляет группу захвата
  • \ 1 в этом контексте совпадает с тем же текстом, который был найден последним при захвате группы номер 1
JMC
источник
1

Извините, я опоздал на 9 лет, но это может сработать для зрителей в 2020 году.

Итак, предположим, у вас есть такая строка "Hello my name is Jello". Теперь вы хотите найти слова, которые начинаются с 'H'и заканчиваются 'o', с любым количеством символов между ними. И мы не хотим строк, мы просто хотим слова. Для этого мы можем использовать выражение:

grep "H[^ ]*o" file

Это вернет все слова. Это работает так: это позволит использовать все символы вместо пробелов между ними, таким образом, мы можем избежать нескольких слов в одной строке.

Теперь вы можете заменить символ пробела на любой другой символ, который вы хотите. Предположим, что исходная строка была "Hello-my-name-is-Jello", тогда вы можете получить слова, используя выражение:

grep "H[^-]*o" file
mr.1n5an_e
источник
0

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

> grep -v -e 'clean\-\?up'
> grep --version grep (GNU grep) 2.20
user200850
источник