Zgrep para após a primeira partida quando os argumentos são passados ​​​​de xargs

Zgrep para após a primeira partida quando os argumentos são passados ​​​​de xargs

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 grepencontrar a primeira correspondência?

PSComo parar o comando find após a primeira partida?não funcionará porque findprecisa 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 .zarquivos .gzcompactados, não arquivos dentro de ziparquivos compactados.

    Às vezes, há um zipgrepscript (quebrado) incluído no unzip, para examinar zipos arquivos, mas o que ele faz é executar egrepem cada membro do arquivo (assim, -m1cada um egreprelataria a primeira correspondência para cada arquivo).

    zgrep, da mesma forma, é um script que acompanha gzipa saída de gzip -cdfqto greppara cada arquivo. gzip -dpode descompactar ziparquivos, mas só o faz para o primeiro membro do arquivo e somente se ele estiver compactado (em ziparquivos, nem todos os membros são necessariamente compactados, especialmente os pequenos).

  • xargsexecuta quantos comandos forem necessários, mas ainda poderá executar vários se a lista de arquivos for grande.

Aqui, sua melhor aposta é provavelmente implementar zipgrepmanualmente (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 zipgrepexecuta zipgrepmuitos 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 -inameem vez de -regex- funciona bem para isso e é, IMO, menos confuso do que findo estranho tratamento de regex. -print0e xargs -0são usados ​​para que quaisquer nomes de arquivos com espaços ou metacaracteres de shell sejam tratados corretamente.

grepA -lopçã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 grepa opção ' --line-bufferede canalizar a saída do grep para head -1. Quando a primeira correspondência for impressa, headirá imprimi-la e terminar, grepnão terá mais um stdout, então terminará e findseguirá.

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

Responder3

grepA opção '(ou zgrep's) -mfará 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 é xargsapenas 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 1lista 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

informação relacionada