Как сопоставить все файлы, содержащие слово1 И слово2 в разных строках, с ag или rg (PCRE/Rust regex)

У меня длинный список сгенерированных отчетов, которые я хочу отфильтровать. Отчет выглядит примерно так:

Report Name
Report Date
Blah blah blah
Blah: WORD1
Blah blah
blah blah: WORD2
blah blah

Я пытаюсь использовать ag (PCRE regex) или rg (rust regex) и найти все файлы, которые содержат WORD1 и WORD2 в разных местах файла (содержит новую строку).

Я уже искал SX и нашел эти, которые не работали:

> ag (?=.*WORD1)(?=.*WORD2)

> ag (?=.*WORD1)((.|\n)*)(?=.*WORD2)

ОБНОВИТЬ

Как отметил @WiktorStribiżew, ag использует PCRE. Извините за ошибку.

мой ожидаемый результат:

blah blah: WORD2

или просто список подходящих файлов.


PS в настоящее время мне удалось использовать это:

> ag "WORD2" $(ag -l "WORD1")

4 ответа

Решение

Вы можете использовать шаблон PCRE с ag:

(?s)^(?=.*WORD1)(?=.*WORD2).*\n\K(?-s).*WORD2

Смотрите демо-версию регулярного выражения.

Детали:

  • (?s) - модификатор DOTALL ON (. соответствует символу перевода строки)
  • ^ - начало строки
  • (?=.*WORD1) - должно быть WORD1 где-то в строке
  • (?=.*WORD2) - должно быть WORD2 где-то в строке
  • .* - любые 0+ символов, как можно больше, до последнего появления последующих подшаблонов (если вы используете ленивый *? квантор, .*? будет соответствовать 0+ символов как можно меньше до первого вхождения последующих подшаблонов)
  • \n - новая строка
  • \K - оператор сброса совпадений, отбрасывающий текущий сопоставленный текст
  • (?-s) - Режим DOTALL отключен (. не соответствует разрывам строк)
  • .*WORD2 - любые 0+ символов, кроме символов разрыва строки, как можно больше, а затем WORD2,

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

ag "WORD2" $(ag -l "WORD1")

Но только WORD2 будет выделен цветом. Я предпочитаю:

ag 'WORD1|WORD2' --passthru -C3 $(ag -l "WORD1" $(ag -l "WORD2"))

Это дает три строки по обе стороны от матчей и выделяет оба WORD1 а также WORD2,

      function agmw() {
  args=("$@")
  qs="ag -l  $1"
  for i in {2..$#}; do
    qs="$qs | xargs -r ag -l '${args[$i]}'"
  done
  argarr="$1"
  for i in {2..$#}; do
    argarr="$argarr|${args[$i]}"
  done
  qs="$qs | xargs -r ag '$argarr'"
  echo $qs
  ag '$argarr'
  bash -c $qs
}

agmw hello world #seacrh привет и мир во всех файлах

PS в настоящее время мне удалось использовать это: ag "WORD2" $(ag -l "WORD1")

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

Если вы используете ack, он имеет -x оператор, который позволяет вам сделать ack -l WORD1 | ack -x WORD2 что в основном то же самое, что ack -l WORD1 | xargs ack WORD2 без необходимости вводить xargs в трубопровод.

Другие вопросы по тегам