Как запустить grep с несколькими шаблонами AND?

86

Я хотел бы получить совпадение нескольких шаблонов с неявным AND между шаблонами, т.е. эквивалентно запуску нескольких greps в последовательности:

grep pattern1 | grep pattern2 | ...

Так как же преобразовать его в нечто подобное?

grep pattern1 & pattern2 & pattern3

Я хотел бы использовать один grep, потому что я строю аргументы динамически, поэтому все должно помещаться в одну строку. Использование фильтра - это системная функция, а не grep, поэтому это не аргумент.


Не путайте этот вопрос с:

grep "pattern1\|pattern2\|..."

Это ИЛИ совпадение с несколькими образцами.

greenoldman
источник
Аналогично:
сопоставить

Ответы:

79

agrep может сделать это с помощью следующего синтаксиса:

agrep 'pattern1;pattern2'

Используя GNU grep, при поддержке PCRE вы можете:

grep -P '^(?=.*pattern1)(?=.*pattern2)'

С астgrep :

grep -X '.*pattern1.*&.*pattern2.*'

(добавление .*s as <x>&<y>соответствует строкам, которые совпадают <x>и <y> точно , a&bи никогда не совпадет , так как нет такой строки, которая может быть одновременно aи bодновременно).

Если шаблоны не перекрываются, вы также можете сделать:

grep -e 'pattern1.*pattern2' -e 'pattern2.*pattern1'

Лучший портативный способ, вероятно, с, awkкак уже упоминалось:

awk '/pattern1/ && /pattern2/'

С sed:

sed -e '/pattern1/!d' -e '/pattern2/!d'

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

Стефан Шазелас
источник
1
agrepСинтаксис не работает для меня ... какая версия была она введена в?
Раман
У @Raman 2.04 с 1992 года это уже было. У меня нет оснований полагать, что этого не было с самого начала. Более новые (после 1992 года) версии agrepмогут быть включены в glimpse / webglimpse . Возможно, у вас есть другая реализация. У меня была ошибка для версии ast-grep, хотя, опция для расширенных регулярных выражений есть -X, нет -A.
Стефан Шазелас
@ StéphaneChazelas Спасибо, у меня agrep0,8.0 на Fedora 23. Похоже, это отличается agrepот того, на который вы ссылаетесь.
Раман
1
@ Раман, твое звучит как TREagrep .
Стефан Шазелас
2
@Techiee, или простоawk '/p1/ && /p2/ {n++}; END {print 0+n}'
Стефан
19

Вы не указали версию grep, это важно. Некоторые механизмы регулярных выражений допускают множественное сопоставление, объединенное оператором AND с использованием '&', но это нестандартная и непереносимая функция. Но, по крайней мере, GNU grep не поддерживает это.

OTOH вы можете просто заменить grep на sed, awk, perl и т. Д. (Перечислены в порядке увеличения веса). С awk команда будет выглядеть так:

awk '/ regexp1 / && / regexp2 / && / regexp3 / {print; }»

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

Netch
источник
3
Просто помните, что awkиспользует ERE, например, эквивалент grep -E, в отличие от BRE, который grepиспользует обычный .
jw013
3
awkРегулярные выражения называются ERE, но на самом деле они немного своеобразные. Вот, вероятно, более подробная информация, чем кому-либо небезразлична: wiki.alpinelinux.org/wiki/Regex
dubiousjim
Спасибо, grep 2.7.3 (openSUSE). Я проголосовал за тебя, но я буду держать вопрос открытым некоторое время, может быть, есть какой-то трюк для grep (не то, что мне не нравится awk- просто знать больше лучше).
Гринольдман
2
Действие по умолчанию состоит в том, чтобы напечатать совпадающую строку, поэтому { print; }деталь здесь не нужна или не нужна.
tripleee
7

Если в patternsкаждой строке содержится один шаблон, вы можете сделать что-то вроде этого:

awk 'NR==FNR{a[$0];next}{for(i in a)if($0!~i)next}1' patterns -

Или это соответствует подстроки вместо регулярных выражений:

awk 'NR==FNR{a[$0];next}{for(i in a)if(!index($0,i))next}1' patterns -

Чтобы напечатать все вместо строк ввода в patternsпустом случае, замените их NR==FNRна FILENAME==ARGV[1]или ARGIND==1на gawk.

Эти функции печатают строки STDIN, которые содержат каждую строку, указанную в качестве аргумента в качестве подстроки. gaвыступает за grep all и gaiигнорирует регистр.

ga(){ awk 'FILENAME==ARGV[1]{a[$0];next}{for(i in a)if(!index($0,i))next}1' <(printf %s\\n "$@") -; }
gai(){ awk 'FILENAME==ARGV[1]{a[tolower($0)];next}{for(i in a)if(!index(tolower($0),i))next}1' <(printf %s\\n "$@") -; }
nisetama
источник
7

Это не очень хорошее решение, но иллюстрирует несколько крутой трюк

function chained-grep {
    local pattern="$1"
    if [[ -z "$pattern" ]]; then
        cat
        return
    fi    

    shift
    grep -- "$pattern" | chained-grep "$@"
}

cat something | chained-grep all patterns must match order but matter dont
olejorgenb
источник
1
Используйте chained-grep()или нет, function chained-grepно function chained-grep(): unix.stackexchange.com/questions/73750/…
nisetama
3

git grep

Вот синтаксис, использующий git grepобъединение нескольких шаблонов с использованием логических выражений:

git grep --no-index -e pattern1 --and -e pattern2 --and -e pattern3

Приведенная выше команда напечатает строки, соответствующие всем шаблонам одновременно.

--no-index Поиск файлов в текущем каталоге, который не управляется Git.

Проверьте man git-grepна помощь.

Смотрите также:

Для операции ИЛИ см .:

kenorb
источник
1

ripgrep

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

rg -N '(?P<p1>.*pattern1.*)(?P<p2>.*pattern2.*)(?P<p3>.*pattern3.*)' file.txt

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

См. Также запрос связанных функций на GH-875 .

kenorb
источник
1

Вот мое мнение, и это работает для слов в несколько строк:

Используйте с find . -type fпоследующим количеством
-exec grep -q 'first_word' {} \;
и последним ключевым словом с
-exec grep -l 'nth_word' {} \;

-qтихие / тихие
-lфайлы шоу со спичками

Далее возвращается список имен файлов со словами «кролик» и «дыра» в них:
find . -type f -exec grep -q 'rabbit' {} \; -exec grep -l 'hole' {} \;

StackRover
источник
-2

Чтобы найти ВСЕ слова (или шаблоны), вы можете запустить grep в цикле FOR . Основным преимуществом здесь является поиск по списку регулярных выражений .

РЕДАКТИРОВАТЬ мой ответ с реальным примером:

# search_all_regex_and_error_if_missing.sh 

find_list="\
^a+$ \
^b+$ \
^h+$ \
^d+$ \
"

for item in $find_list; do
   if grep -E "$item" file_to_search_within.txt 
   then
       echo "$item found in file."
   else
       echo "Error: $item not found in file. Exiting!"
       exit 1
   fi
done

Теперь давайте запустим этот файл:

hhhhhhhhhh

ааааааа

bbbbbbbbb

ababbabaabbaaa

CCCCCCC

dsfsdf

BBBB

cccdd

аа

CAA

# ./search_all_regex_and_error_if_missing.sh

ааааааа аа

^ a + $ найдено в файле.

bbbbbbbbb bbbb

^ b + $ найдено в файле.

hhhhhhhhhh

^ h + $ найдено в файле.

Ошибка: ^ d + $ не найден в файле. Выход!

Ноам Манос
источник
1
Ваша логика неверна - я спросил ALLоператора, ваш код работает как ORоператор, а не AND. И кстати. для этого ( OR) гораздо более простое решение, заданное прямо в вопросе.
Гринольдман
@greenoldman Логика проста: for будет зацикливаться на ВСЕХ словах / шаблонах в списке, а если он будет найден в файле - напечатает его. Так что просто удалите else, если вам не нужны действия, если слово не найдено.
Ноам Манос
1
Я понимаю вашу логику так же, как и мой вопрос - я спрашивал об ANDоператоре, то есть файл является только положительным попаданием, если он соответствует шаблону A и шаблону B и шаблону C и ... ANDВ вашем случае файл имеет положительный результат, если он соответствует образец A или образец B или ... Вы видите разницу сейчас?
Гринольдман
@greenoldman не уверен, почему вы думаете, что этот цикл не проверяет И условие для всех шаблонов? Таким образом, я отредактировал свой ответ с реальным примером: он будет искать в файле все регулярные выражения списка, а по первому отсутствующему - завершится с ошибкой.
Ноам Манос
У вас есть это прямо перед вашими глазами, у вас есть положительный матч сразу после того, как первый матч будет выполнен. Вы должны были "собрать" все результаты и вычислить ANDих. Затем вам следует переписать сценарий для работы с несколькими файлами - тогда, возможно, вы поймете, что на вопрос уже дан ответ, и ваша попытка ничего не приносит на стол, извините.
Гринольдман