grep или ripgrep: как найти только файлы, соответствующие нескольким шаблонам (а не только в одной строке)?
Я ищу быстрый способ найти все файлы в папке, содержащие 2 или более шаблонов
grep -l -e foo -e bar ./*
илиrg -l -e foo -e bar
показать все файлы, содержащие 'foo' И 'bar' в одной строке или 'foo' ИЛИ 'bar' в разных строках, но мне нужны только файлы, которые имеют как минимум одно совпадение 'foo' И одно совпадение 'bar' в разных строках. Файлы, в которых есть только совпадения "foo" или только совпадения "bar", должны быть отфильтрованы.
Я знаю, что могу связать вызовы grep, но это будет слишком медленно.
5 ответов
Так что это не совсем отвечает на вопрос, но это вопрос StackOverflow, который появляется каждый раз, когда я гуглю «множественные шаблоны ripgrep». Поэтому я оставляю свой ответ здесь для будущего гуглера (включая себя)...
В основном я работаю в PowerShell, поэтому я выполняю
and
поиск в ripgrep в PowerShell. Это будет соответствовать одинаковым совпадениям строк, поэтому это не идеальный ответ, но он идентифицирует файлы, которые соответствуют обоим шаблонам, и работает относительно быстро:
rg -l 'SecondSearchPattern' (rg -l 'FirstSearchPattern')
Объяснение:
Сначала бегут родители:
rg -l 'FirstSearchPattern'
, который ищет шаблон во всех файлахFirstSearchPattern
. Используя-l
он возвращает только список путей к файлам.Поместив его в
(
скобки)
, он сначала запускает всю команду, а затем "выводит" результаты команды во внешнюю команду.Внешний
rg
команда теперь выполняется так:rg -l 'SecondSearchPattern' "file.txt" "directory\file.txt"
И да, он заключает их в кавычки, поэтому обрабатывает пути с пробелами. Это ищет все предоставленные файлы, которые соответствуют шаблону
SecondSearchPattern
. Таким образом, возвращаются только файлы, соответствующие обоим шаблонам.
Вы можете сделать еще один шаг и добавить
| Get-Item
(
| gi
) для возврата объектов файловой системы и
| % FullName
чтобы получить полный путь.
rg -l 'SecondSearchPattern' (rg -l 'FirstSearchPattern') | gi | % FullName
rg
с
multiline
работает, однако в результате будет напечатано все, что находится между критериями, а иногда это бесполезно.
Для случая использования цепочек поисков (например,
html, json
и т.д.), где 1-й критерий предназначен только для сужения файлов, а 2-й критерий - это то, что я ищу, это возможное решение:
rg -0 -l crit1 | xargs -0 -I % rg -H crit2 %
В качестве альтернативы я только что обнаружил
ugrep
который поддерживает объединение нескольких критериев с использованием логических операторов как на уровне строки , так и на уровне файла . Это нечто. Это немного медленнее, чем
rg + xargs
, однако он хорошо печатает все строки, соответствующие всем критериям из файлов (вместо того, чтобы просто показывать последние критерии сверху):
ugrep --files -e crit1 --and -e crit2
Если вы хотите найти два или более слов, которые встречаются в нескольких строках, вы можете использовать
ripgrep
вариант
--multiline-dotall
, в дополнение к предоставлению
-U
/
--multiline
. Вам также нужно искать до и
bar
до
foo
с помощью оператора:
rg -lU --multiline-dotall 'foo.*bar|bar.*foo' .
Для любого количества слов вам нужно
|
все перестановки этих слов. Для этого я использую небольшой скрипт на Python (который я назвал
rga
), который ищет в текущем каталоге (и ниже) файлы, содержащие все аргументы, указанные в командной строке:
#! /opt/util/py310/bin/python
import sys
import subprocess
from itertools import permutations
rgarg = '|'.join(('.*'.join(x) for x in permutations(sys.argv[1:])))
cmd = ['rg', '-lU', '--multiline-dotall', rgarg, '.']
# print(' '.join(cmd))
proc = subprocess.run(cmd, capture_output=True)
sys.stdout.write(proc.stdout.decode('utf-8'))
Я успешно выполнил поиск с шестью аргументами, выше этого командная строка становится слишком длинной. Вероятно, есть способы обойти это, сохранив аргумент в файл и добавив
-f file_name
, но я никогда не нуждался в этом и не исследовал его.
$ cat f1
afoot
2bar
$ cat f2
foo bar
$ cat f3
foot
$ cat f4
bar
$ cat f5
barred
123
foo3
$ rg -Ul '(?s)foo.*?\n.*?bar|bar.*?\n.*?foo'
f5
f1
Ты можешь использовать -U
возможность совпадения по строкам. Вs
флаг позволит .
для соответствия новым строкам. Поскольку вы хотите, чтобы совпадения находились в разных строках, вам также необходимо сопоставить символ новой строки между условиями поиска.
вы можете добавить следующую функцию: (проверено в zsh)
multisearch() {
case $# in
0) return 1 ;;
1) rg $1 ;;
esac
local lastArg=${@[${#}]}
local files=(`rg --files-with-matches ${1}`)
(( ${#files} )) || return 0
# skip first and last arg
for arg in ${@:2:# - 2}; do
files=(`rg --files-with-matches ${arg} ${files[@]}`)
(( ${#files} )) || return 0
done
rg ${lastArg} ${files[@]}
}
и используйте как:
$ multisearch foo bar