usando xargs para grep múltiples patrones

12

Tengo un archivo que tiene términos que quiero grep, con cada término siendo una línea en el archivo. Estaba pensando que podría hacer esto con xargs. Lo que puedo deducir de ejemplos de la página de manual como este

find ./work -print0 | xargs -0 rm

es que xargs agrega la salida del comando pre-pipe al final de sus argumentos. Entonces, si el hallazgo regresó report.doc, entonces xargs se construiría rm report.doc. ¿Es correcto este entendimiento?

Entonces, dado que quiero que los valores en mi archivo estén en el medio del comando grep, necesito especificar un marcador de posición. Al jugar, lo intenté {}, pero no funcionó:

$> cat strings.txt | xargs grep {} subdirectory/*
grep: string1: No such file or directory
grep: string2: No such file or directory

¿Es xargs la herramienta correcta? Si es así, ¿cuál es la sintaxis?

usuario394
fuente

Respuestas:

17

Sí, find ./work -print0 | xargs -0 rmejecutará algo como rm ./work/a "work/b c" .... Puede verificar con echo, find ./work -print0 | xargs -0 echo rmimprimirá el comando que se ejecutará (excepto que el espacio en blanco se escapará adecuadamente, aunque echono lo mostrará).

Para xargsponer los nombres en el medio, debe agregar -I[string], donde [string]es lo que desea reemplazar con el argumento, en este caso usaría -I{}, por ejemplo <strings.txt xargs -I{} grep {} directory/*.

Lo que realmente quieres usar es grep -F -f strings.txt:

-F, --fixed-strings
  Interpret PATTERN as a  list  of  fixed  strings,  separated  by
  newlines,  any  of  which is to be matched.  (-F is specified by
  POSIX.)
-f FILE, --file=FILE
  Obtain  patterns  from  FILE,  one  per  line.   The  empty file
  contains zero patterns, and therefore matches nothing.   (-f  is
  specified by POSIX.)

Por grep -Ff strings.txt subdirectory/*lo tanto , encontrará todas las apariciones de cualquier cadena en strings.txtun literal, si elimina la -Fopción, puede usar expresiones regulares en el archivo. Realmente podrías usar grep -F "$(<strings.txt)" directory/*también. Si quieres practicar find, puedes usar los dos últimos ejemplos en el resumen. Si desea hacer una búsqueda recursiva en lugar de solo el primer nivel, tiene algunas opciones, también en el resumen.

Resumen:

# grep for each string individually.
<strings.txt xargs -I{} grep {} directory/*

# grep once for everything
grep -Ff strings.txt subdirectory/*
grep -F "$(<strings.txt)" directory/*

# Same, using file
find subdirectory -maxdepth 1 -type f -exec grep -Ff strings.txt {} +
find subdirectory -maxdepth 1 -type f -print0 | xargs -0 grep -Ff strings.txt

# Recursively
grep -rFf strings.txt subdirectory
find subdirectory -type f -exec grep -Ff strings.txt {} +
find subdirectory -type f -print0 | xargs -0 grep -Ff strings.txt

Es posible que desee utilizar la -lopción para obtener solo el nombre de cada archivo coincidente si no necesita ver la línea real:

-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.  (-l is specified by
  POSIX.)
Kevin
fuente
4

xargsPuede que no sea la mejor herramienta que sugeriría fgrepsi su archivo de cadenas para coincidir solo contiene cadenas, no expresiones regulares.

fgrep -f strings.txt subdirectory/*

Sugiero fgrepcomo Unix tradicional grepy egrepno tenía la opción "-f". Creo que GNU grepy egreptengo la opción "-f", por lo que si tu archivo tiene expresiones regulares, querrás usar una versión de GNU.

Bruce Ediger
fuente