Zgrep pára após a primeira correspondência quando os argumentos são passados de xargs

2

Eu estou usando este comando para encontrar padrões em arquivos zip (semelhante ao um) sugerido aqui link

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?

P.S. Como parar o comando find após a primeira partida? não funcionará porque find precisa ser interrompido após uma correspondência que tenha êxito grep e não apenas a primeira correspondência de find.

    
por user13107 19.09.2013 / 07:56

4 respostas

3

Várias coisas:

  • zgrep é procurar em .z ou .gz arquivos compactados, não em arquivos compactados zip archives.

    Há um script (quebrado) zipgrep às vezes empacotado com unzip , para pesquisar zip archives, mas o que ele faz é executar egrep em cada membro do archive (portanto, com -m1 each egrep informaria a primeira correspondência para cada arquivo).

    zgrep , similarmente é um script que vem com gzip que alimenta a saída de gzip -cdfq to grep para cada arquivo. gzip -d pode descompactar zip files, mas apenas para o primeiro membro do archive e somente se ele for compactado (em zip files, nem todos os membros são necessariamente compactados, especialmente os pequenos).

  • xargs executa quantos comandos forem necessários, mas ainda pode rodar vários se a lista de arquivos for grande.

Aqui, sua melhor aposta é provavelmente implementar zipgrep manualmente (aqui com as 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 e zipgrep executam muito mais comandos.

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 .

    
por 19.09.2013 / 16:01
2

Tente:

find . -iname '*.zip' -print0 | xargs -0r zgrep -l -E 'PATTERN'

Eu usei -iname em vez de -regex - ele funciona bem para isso e é, IMO, menos confuso do que o estranho controle de regex de find . -print0 e xargs -0 são usados para que qualquer nome de arquivo com espaços ou metacaracteres de shell seja manipulado corretamente.

A opção grep do -l está documentada na página man:

   -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. note que isso significa que o grep continuará pesquisando os outros arquivos, mesmo depois de ter encontrado uma correspondência.

Se você quiser que ele pare após a primeira correspondência, você pode usar a opção grep --line-buffered e a saída do pipe grep em head -1 . Quando a primeira correspondência for impressa, head irá imprimi-la e finalizar, grep não terá mais um stdout, por isso será encerrado e find seguirá.

find . -iname '*.zip' -print0 | xargs -0r zgrep --line-buffered -l -E 'PATTERN' | head -1
    
por 19.09.2013 / 08:06
1
A opção de grep (ou zgrep ') -m fará com que pare de ler o arquivo atual na primeira correspondência:

   -m NUM, --max-count=NUM
          Stop reading a file after NUM matching lines.  

Isso não impedirá que ele pesquise no arquivo próximo . Por exemplo:

$ echo "hello" > foo
$ echo "hello" > bar
$ grep -m 1 hello foo bar
foo:hello
bar:hello

Portanto, o problema não é xargs , mas o fato de você estar usando vários arquivos. Para que grep (ou zgrep ) pare após o primeiro arquivo correspondente , você teria que executar um pequeno loop como sugerido por @Stephane. Ou algo parecido com o bash:

shopt -s globstar
for i in **/*.zip; do
  zgrep -l pattern "$i" && break; 
done

Ou, para arquivos zip, que conté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
    
por 19.09.2013 / 16:58
0

grep -m 1 lista a primeira correspondência de todos os arquivos.

Há uma maneira fácil de listar apenas a primeira correspondência: canalizar por head -n 1 . A busca morrerá em breve de um SIGPIPE .

find . -regex ".*/.*zip" -print0 | xargs -0 zgrep -E "PATTERN" | head -n 1
    
por 20.09.2013 / 01:37