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


86

Я хотів би отримати збіг декількох шаблонів із неявними AND між шаблонами, тобто еквівалентним виконанню декількох греппів у послідовності:

grep pattern1 | grep pattern2 | ...

Тож як перетворити це на щось подібне?

grep pattern1 & pattern2 & pattern3

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


Не плутайте це питання з:

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

Це збірка АБО з декількома моделями.



Відповіді:


78

agrep можна зробити це за допомогою цього синтаксису:

agrep 'pattern1;pattern2'

За допомогою GNU grepпри створенні з підтримкою PCRE ви можете:

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

З астgrep :

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

(додаючи .*s як <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можна знайти, включені з проглядом / webglimpse . Можливо, у вас є інша реалізація. У мене була помилка у версії ast-grep, але варіант для розширених регулярних виразів є -X, ні -A.
Стефан Шазелас

@ StéphaneChazelas Спасибі, у мене agrepна Fedora 23. у мене 0,8.0. Здається, це відрізняється agrepвід того, про який ви посилаєтесь.
Раман

1
@Raman, твій звучить як TREagrep .
Стефан Шазелас

2
@Techiee, або простоawk '/p1/ && /p2/ {n++}; END {print 0+n}'
Stéphane Chazelas

19

Ви не вказали grep-версію, це важливо. Деякі двигуни regexp дозволяють кілька разів узгоджувати групування І за допомогою "&", але це нестандартна та не портативна функція. Але, принаймні, GNU grep це не підтримує.

OTOH ви можете просто замінити греп на sed, awk, perl тощо (перераховано в порядку збільшення ваги). З awk, команда виглядала б так

awk '/ regexp1 / && / regexp2 / && / regexp3 / {друк; } '

і його можна побудувати так, щоб в командному рядку було визначено простий спосіб.


3
Пам'ятайте лише, що awkвикористовує ERE, наприклад еквівалент grep -E, на відміну від BRE, який grepвикористовує звичайна .
jw013

3
awkрегулярні вирази «s є називаються Eres, але насправді вони трохи своєрідними. Ось, мабуть, більше деталей, ніж хтось цікавить: wiki.alpinelinux.org/wiki/Regex
сумнівний

Дякую, греп 2.7.3 (openSUSE). Я підтримав вас, але я буду тримати питання відкритим на деякий час, можливо, є якась хитрість до грепу (не те, що мені не подобається awk- просто краще знати більше).
greenoldman

2
Дія за замовчуванням - це друк відповідного рядка, тому { print; }частина тут насправді не потрібна чи корисна.
трійчатка

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виступає за греп усіх і 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 "$@") -; }

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

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за допомогою.

Дивитися також:

Про операцію АБО див.


1

ripgrep

Ось приклад із використанням rg:

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

Це один з найшвидших інструментів прихватування, оскільки він побудований на основі регекс- двигуна Руста, який використовує кінцеві автомати, SIMD та агресивні буквальні оптимізації, щоб зробити пошук дуже швидким.

Дивіться також відповідний запит на функції на GH-875 .


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' {} \;


-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

абаббабааббааа

ccccccc

dsfsdf

bbbb

cccdd

аа

каа

# ./search_all_regex_and_error_if_missing.sh

aaaaaaa aa

^ a + $ знайдено у файлі.

bbbbbbbbb bbbb

^ b + $ знайдено у файлі.

hhhhhhhhhh

^ h + $ знайдено у файлі.

Помилка: ^ d + $ не знайдено у файлі. Вихід!


1
Ваша логіка несправна - я попросив ALLоператора, ваш код працює як ORоператор, а не AND. І btw. для цього ( OR) набагато простіше рішення, дане прямо у питанні.
greenoldman

@greenoldman Логіка проста: The for зациклюється на ВСІХ словах / шаблонах у списку, і якщо він знайдеться у файлі - надрукує його. Тому просто видаліть інше, якщо вам не потрібні дії, якщо слово не було знайдено.
Ноам Манос

1
Я розумію вашу логіку, а також моє запитання - я запитував про ANDоператора, тобто файл - це лише позитивне враження, якщо він відповідає шаблону A і шаблону B і шаблону C і ... ANDУ вашому випадку файл позитивного удару, якщо він відповідає візерунок A або візерунок B або ... Чи бачите ви зараз різницю?
greenoldman

@greenoldman не впевнений, чому ви вважаєте, що ця петля не перевіряє І умова для всіх шаблонів? Тому я відредагував свою відповідь реальним прикладом: Він буде шукати у файлі весь регекс списку, а на першому, який відсутній, - вийде з помилкою.
Ноам Манос

У вас це прямо перед очима, ви маєте позитивну відповідність відразу після першого матчу. Ви повинні "зібрати" всі результати та обчислити ANDїх. Тоді вам слід переписати сценарій для запуску на декількох файлах - тоді, можливо, ви зрозумієте, що на питання вже відповіли, і ваша спроба нічого не подає до таблиці, вибачте.
greenoldman
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.