Zgrep se detiene después del primer partido cuando se pasan argumentos desde xargs

Zgrep se detiene después del primer partido cuando se pasan argumentos desde xargs

Estoy usando este comando para buscar patrones en archivos zip (similar al) sugerido aquí 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"

El grepping continúa después del primer partido. Probablemente findyo xargssea el culpable. ¿Cómo dejar de buscar después grepde encontrar la primera coincidencia?

PD¿Cómo detener el comando de búsqueda después de la primera coincidencia?no funcionará porque finddebe detenerse después de una coincidencia que tenga éxito con grep y no solo con la primera coincidencia de find.

Respuesta1

Varias cosas:

  • zgrepes buscar .zarchivos .gzcomprimidos, no archivos dentro de ziparchivos comprimidos.

    A veces hay un zipgrepscript (roto) incluido con unzip, para buscar ziparchivos, pero lo que hace es ejecutarse egrepen cada miembro del archivo (por lo que -m1cada uno egrepinformaría la primera coincidencia para cada archivo).

    zgrep, de manera similar, viene con un script que gzipalimenta la salida de gzip -cdfqpara grepcada archivo. gzip -dPuede descomprimir ziparchivos, pero sólo lo hace para el primer miembro del archivo y sólo si está comprimido (en ziplos archivos, no todos los miembros están necesariamente comprimidos, especialmente los pequeños).

  • xargsejecuta tan pocos comandos como sea necesario, pero aún puede ejecutar varios si la lista de archivos es grande.

En este caso, lo mejor que puede hacer probablemente sea implementarlo zipgrepmanualmente (aquí con herramientas 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

Eso ejecuta un shell por archivo, pero también lo haría zipgrepy zipgrepejecuta muchos más comandos.

Puede fallar si los miembros del archivo tienen nombres que contienen caracteres comodín ( *, [, ?) u otros caracteres como caracteres ASCII 0x1 a 0x1f y varios otros, pero eso se debe principalmente a errores y limitaciones en unzip, y eso no es tan malo como cuando se usa zipgrep.

Respuesta2

Intentar:

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

He usado -inameen lugar de -regex: funciona también para esto y, en mi opinión, es menos confuso que findel extraño manejo de expresiones regulares. -print0y xargs -0se utilizan para que cualquier nombre de archivo con espacios o metacaracteres de shell se maneje correctamente.

grepLa -lopción está documentada en la 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.

La primera coincidencia mencionada es por archivo, por lo que si coinciden varios archivos, se imprimirán todos. tenga en cuenta que esto significa que grep continuará buscando otros archivos, incluso después de haber encontrado una coincidencia.

Si desea que se detenga después de la primera coincidencia, puede usar grepla --line-bufferedopción y canalizar la salida de grep a head -1. Cuando se imprima la primera coincidencia, headla imprimirá y finalizará, grepya no tendrá una salida estándar, por lo que terminará y findseguirá.

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

Respuesta3

grepLa opción 's (o zgrep's) -mhará que deje de leer elarchivo actualen el primer partido:

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

Eso no le impedirá buscar en elpróximoarchivo. Por ejemplo:

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

Entonces, el problema no es xargsmás que el hecho de que estás recopilando varios archivos. Para que grep(o zgrep) se detenga después del primer emparejamientoarchivo, tendrías que ejecutar un pequeño bucle como sugirió @Stephane. O algo como esto con bash:

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

O, para archivos zip quecontener varios archivos(gracias @Stephane):

shopt -s globstar
for i in **/*.zip; do
  if unzip -p "$i" | grep -q hello; then 
    echo "$i" && break;
  fi;
done

Respuesta4

grep -m 1enumera la primera coincidencia de cada archivo.

Hay una forma sencilla de enumerar solo la primera coincidencia: canalizar head -n 1. La búsqueda pronto morirá de unSIGPIPE.

find . -regex ".*/.*zip" -print0 | xargs -0 zgrep -E "PATTERN" | head -n 1

información relacionada