
Я использую эту команду для поиска шаблонов в zip-файлах (похожих на предложенные здесь) https://superuser.com/questions/144926/unix-grep-for-a-string-within-all-gzip-files-in-all-subdirectories
find . -regex ".*/.*zip" | xargs zgrep -m 1 -E "PATTERN"
Grepping все еще продолжается после первого совпадения. Вероятно, find
/ xargs
является виновником. Как остановить поиск после того, grep
как находит первое совпадение?
ПСКак остановить команду find после первого совпадения?не сработает, так как find
его необходимо остановить после успешного совпадения grep, а не только после первого совпадения find.
решение1
Несколько вещей:
zgrep
заключается в просмотре.z
сжатых.gz
файлов, а не файлов внутри сжатыхzip
архивов.zipgrep
Иногда в комплекте с , идет (сломанный) скриптunzip
для просмотраzip
архивов, но он запускаетсяegrep
для каждого члена архива (поэтому для-m1
каждогоegrep
будет выведено первое совпадение для каждого файла).zgrep
, аналогично — это скрипт, который поставляется сgzip
, который передает выводgzip -cdfq
вgrep
для каждого файла.gzip -d
может распаковыватьzip
файлы, но делает это только для первого элемента архива и только если он сжат (вzip
файлах не все элементы обязательно сжаты, особенно небольшие).xargs
запускает столько команд, сколько необходимо, но может запустить и несколько, если список файлов большой.
Здесь лучшим вариантом, вероятно, будет реализация zipgrep
вручную (в данном случае с помощью инструментов GNU):
find . -name '*.zip' -type f -exec sh -c '
unzip -Z1 "$1" |
while IFS= read -r file; do
unzip -p "$1" "$file" | grep --label="$1//$file" -Hm1 -- "$0" && exit
done' PATTERN {} \; -quit
Это запускает одну оболочку для каждого файла, но так же будет запускаться zipgrep
и zipgrep
гораздо больше команд.
Он может не сработать, если имена членов архива содержат подстановочные знаки ( *
, [
, ?
) или другие символы, такие как символы ASCII от 0x1 до 0x1f и различные другие, но это в основном из-за ошибок и ограничений в unzip
, и это не так плохо, как при использовании zipgrep
.
решение2
Пытаться:
find . -iname '*.zip' -print0 | xargs -0r zgrep -l -E 'PATTERN'
Я использовал -iname
вместо этого -regex
— это тоже работает и, на мой взгляд, менее запутанно, чем find
странная обработка регулярных выражений. -print0
и xargs -0
используются для того, чтобы любые имена файлов с пробелами или метасимволами оболочки в них обрабатывались правильно.
grep
Параметр 's -l
задокументирован на странице руководства:
-l, --files-with-matches
Suppress normal output; instead print the name of each input
file from which output would normally have been printed. The
scanning will stop on the first match.
Первое указанное совпадение относится к каждому файлу, поэтому, если совпадет несколько файлов, они все будут выведены. Обратите внимание, что это означает, что grep продолжит поиск в других файлах, даже после того, как найдет одно совпадение.
Если вы хотите, чтобы он остановился после самого первого совпадения, вы можете использовать grep
опцию --line-buffered
и передать вывод grep в head -1
. Когда будет выведено первое совпадение, head
выведет его и завершит работу, grep
больше не будет иметь stdout, поэтому завершит работу, и find
последует за ним.
find . -iname '*.zip' -print0 | xargs -0r zgrep --line-buffered -l -E 'PATTERN' | head -1
решение3
grep
опция (или zgrep
) -m
заставит его прекратить чтениетекущий файлв первом матче:
-m NUM, --max-count=NUM
Stop reading a file after NUM matching lines.
Это не помешает ему искатьследующийфайл. Например:
$ echo "hello" > foo
$ echo "hello" > bar
$ grep -m 1 hello foo bar
foo:hello
bar:hello
Итак, проблема не в xargs
том, что вы сканируете несколько файлов. Для того, чтобы иметь grep
(или zgrep
) остановиться после первого совпаденияфайл, вам придется запустить небольшой цикл, как предложил @Stephane. Или что-то вроде этого с bash :
shopt -s globstar
for i in **/*.zip; do
zgrep -l pattern "$i" && break;
done
Или для zip-архивов, которыесодержать несколько файлов(спасибо @Stephane):
shopt -s globstar
for i in **/*.zip; do
if unzip -p "$i" | grep -q hello; then
echo "$i" && break;
fi;
done
решение4
grep -m 1
выводит список первых совпадений каждого файла.
Есть простой способ вывести только первое совпадение: pipe through head -n 1
. Поиск скоро умрет отСИГПАЙП.
find . -regex ".*/.*zip" -print0 | xargs -0 zgrep -E "PATTERN" | head -n 1