
Estou usando este comando para encontrar padrões em arquivos zip (semelhantes ao sugerido aqui) 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 ainda continua após a primeira partida. Provavelmente find
/ xargs
é o culpado. Como parar de encontrar depois de grep
encontrar a primeira correspondência?
PSComo parar o comando find após a primeira partida?não funcionará porque find
precisa ser interrompido após uma correspondência bem-sucedida no grep e não apenas na primeira correspondência do find.
Responder1
Varias coisas:
zgrep
é examinar.z
arquivos.gz
compactados, não arquivos dentro dezip
arquivos compactados.Às vezes, há um
zipgrep
script (quebrado) incluído nounzip
, para examinarzip
os arquivos, mas o que ele faz é executaregrep
em cada membro do arquivo (assim,-m1
cada umegrep
relataria a primeira correspondência para cada arquivo).zgrep
, da mesma forma, é um script que acompanhagzip
a saída degzip -cdfq
togrep
para cada arquivo.gzip -d
pode descompactarzip
arquivos, mas só o faz para o primeiro membro do arquivo e somente se ele estiver compactado (emzip
arquivos, nem todos os membros são necessariamente compactados, especialmente os pequenos).xargs
executa quantos comandos forem necessários, mas ainda poderá executar vários se a lista de arquivos for grande.
Aqui, sua melhor aposta é provavelmente implementar zipgrep
manualmente (aqui com ferramentas 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
Isso executa um shell por arquivo, mas também zipgrep
executa zipgrep
muitos outros comandos.
Ele pode falhar se os membros do arquivo tiverem nomes que contenham caracteres curinga ( *
, [
, ?
) ou outros caracteres como caracteres ASCII 0x1 a 0x1f e vários outros, mas isso se deve principalmente a bugs e limitações em unzip
, e isso não é tão ruim quanto ao usar zipgrep
.
Responder2
Tentar:
find . -iname '*.zip' -print0 | xargs -0r zgrep -l -E 'PATTERN'
Eu usei -iname
em vez de -regex
- funciona bem para isso e é, IMO, menos confuso do que find
o estranho tratamento de regex. -print0
e xargs -0
são usados para que quaisquer nomes de arquivos com espaços ou metacaracteres de shell sejam tratados corretamente.
grep
A -l
opção está documentada na página de manual:
-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.
A primeira correspondência mencionada é por arquivo, portanto, se vários arquivos corresponderem, todos serão impressos. observe que isso significa que o grep continuará pesquisando os outros arquivos, mesmo depois de encontrar uma correspondência.
Se você quiser que ele pare após a primeira partida, você pode usar grep
a opção ' --line-buffered
e canalizar a saída do grep para head -1
. Quando a primeira correspondência for impressa, head
irá imprimi-la e terminar, grep
não terá mais um stdout, então terminará e find
seguirá.
find . -iname '*.zip' -print0 | xargs -0r zgrep --line-buffered -l -E 'PATTERN' | head -1
Responder3
grep
A opção '(ou zgrep
's) -m
fará com que ele pare de ler oarquivo atualna primeira partida:
-m NUM, --max-count=NUM
Stop reading a file after NUM matching lines.
Isso não o impedirá de procurar opróximoarquivo. Por exemplo:
$ echo "hello" > foo
$ echo "hello" > bar
$ grep -m 1 hello foo bar
foo:hello
bar:hello
Portanto, o problema não é xargs
apenas o fato de você estar usando vários arquivos. Para que grep
(ou zgrep
) pare após a primeira correspondênciaarquivo, você teria que executar um pequeno loop como @Stephane sugeriu. Ou algo assim com bash :
shopt -s globstar
for i in **/*.zip; do
zgrep -l pattern "$i" && break;
done
Ou, para arquivos zip quecontém vários arquivos(obrigado @Stephane):
shopt -s globstar
for i in **/*.zip; do
if unzip -p "$i" | grep -q hello; then
echo "$i" && break;
fi;
done
Responder4
grep -m 1
lista a primeira correspondência de cada arquivo.
Existe uma maneira fácil de listar apenas a primeira correspondência: pipe through head -n 1
. A busca logo morrerá de umSIGPIPE.
find . -regex ".*/.*zip" -print0 | xargs -0 zgrep -E "PATTERN" | head -n 1