¿Xargs en el segundo lado de la tubería?

¿Xargs en el segundo lado de la tubería?

Estoy intentando hacer lo siguiente:

cat file1.txt | xargs -I{} "cat file2.txt | grep {}"

Espero que cada línea del archivo 1 sea el valor de grep al final del tercer tubo. No funciona como se esperaba.

¿Esto se debe a que -I{}deja de buscar cosas para reemplazar una vez que llega a la tubería? ¿Hay alguna forma de evitar esto?

Respuesta1

Es porque necesita un shell para crear una tubería o realizar una redirección. Tenga en cuenta que este cates el comando para concatenar, no tiene mucho sentido usarlo solo para un archivo.

cat file1.txt | xargs -I{} sh -c 'cat file2.txt | grep -e "$1"' sh {}

Hacernohacer:

archivo gato1.txt | xargs -I{} sh -c 'cat archivo2.txt | grep -e {}'

ya que eso equivaldría a una vulnerabilidad de inyección de comandos. Se {}ampliaría en el argumento del código para shinterpretarlo como código de shell. Por ejemplo, si la línea de uno file1.txtfuera $(reboot)esa, llamaría reboot.

El -e(o también podrías usar --) también es importante. Sin él, tendrías problemas con las expresiones regulares que comienzan con -.

Puedes simplificar lo anterior usando redirecciones en lugar de cat:

< file1.txt xargs -I{} sh -c '< file2.txt grep -e "$1"' sh {}

O simplemente pase los nombres de los archivos como argumento grepen lugar de usar redirecciones, en cuyo caso puede incluso eliminar sh:

< file1.txt xargs -I{} grep -e {} file2.txt

También podría indicar grepbuscar todas las expresiones regulares a la vez en una sola invocación:

grep -f file1.txt file2.txt

Sin embargo, tenga en cuenta que en ese caso, es solo una expresión regular para cada línea de file1.txt, no hay ningún procesamiento de cotización especial realizado por xargs.

xargsde forma predeterminada, considera su entrada como una lista de palabras en blanco (en algunas implementaciones solo espacio y tabulación, en otras, cualquiera en la [:blank:]clase de caracteres de la configuración regional actual) o palabras separadas por nueva línea para las cuales se pueden usar barras invertidas y comillas simples y dobles para escapar de los separadores. (Sin embargo, solo se puede escapar de la nueva línea mediante una barra invertida) o entre sí.

Por ejemplo, en una entrada como:

 'a "b'\" "bar baz" x\
y

xargssin -I{}pasaría a "b", bar bazy x<newline>yal mando.

Con -I{}, xargsobtiene una palabra por línea pero aún realiza un procesamiento adicional. Ignora los espacios en blanco iniciales (pero no finales). Los espacios en blanco ya no se consideran separadores, pero aún se está procesando la cotización.

En la entrada anterior xargs -I{}se pasaría un a "b" foo bar x<newline>yargumento al comando. También tenga en cuenta que muchos sistemas, como lo exige POSIX, no funcionarán si las palabras tienen más de 255 caracteres. Considerándolo todo, xargs -I{}es bastante inútil.

Si desea que cada línea se pase palabra por palabra como argumento del comando, puede usar la xargs -d '\n'extensión GNU:

< file1.txt xargs -d '\n' -n 1 grep file2.txt -e

(aquí confiando en otra extensión de GNU grepque permite pasar opciones después de los argumentos (siempre que POSIXly correcto no esté en el entorno) o de forma portátil:

sed "s/'/'\\\\\\''/g;s/.*/'&'/" file1.txt | xargs -n1 sh -c '
  for line do
    grep -e "$line" file2.txt
  done' sh

Si quisieras cada unopalabraen file1.txt(comillas aún reconocidas) en contraposición a cada unalíneapara ser buscado (lo que también solucionaría el problema del espacio final si de todos modos tiene una palabra por línea), puede usarlo xargs -n1solo en lugar de usar -I:

< file1.txt xargs -n1 sh -c '
  for word do
    grep -e "$word" file2.txt
  done' sh

Para eliminar los espacios en blanco iniciales y finales (pero sin el procesamiento de cotizaciones que xargslo hace), también puede hacer:

unset IFS # restore word splitting to its default
while read -r regexp; do
  grep -e "$regexp" file2.txt
done < file1.txt

Respuesta2

Dependiendo de lo que intente hacer, es mejor que lo omita xargspor completo y opte por esta solución:

grep -f file1.txt file2.txt

Esto difiere de su comando original.(una vez que lo arreglemos como en la respuesta de Stéphane Chazelas) de la siguiente manera:

  • Las líneas se imprimen en el orden en que aparecen, file2.txtindependientemente de los patrones con los que coincidan. En su comando, se imprimen todas las líneas que coinciden con el primer patrón, luego todas las líneas que coinciden con el segundo, y así sucesivamente.
  • Las líneas que coinciden con más de un patrón se imprimen exactamente una vez. A tu disposición, se imprimen una vez por cada patrón que coincida.
  • Se pueden usar varias banderas más fácilmente, incluidas ambas -vy -c.

la -fbandera esespecificado por POSIXy por lo tanto razonablemente portátil.

información relacionada