¿Cómo usar> en un comando xargs?

160

Quiero encontrar un comando bash que me permita grep cada archivo en un directorio y escribir la salida de ese grep en un archivo separado. Supongo que habría sido hacer algo como esto

ls -1 | xargs -I{} "grep ABC '{}' > '{}'.out"

pero, que yo sepa, a xargs no le gustan las comillas dobles. Sin embargo, si elimino las comillas dobles, el comando redirige la salida del comando completo a un solo archivo llamado '{}'. En lugar de a una serie de archivos individuales.

¿Alguien sabe de una manera de hacer esto usando xargs? Acabo de usar este escenario grep como ejemplo para ilustrar mi problema con xargs, por lo que cualquier solución que no use xargs no es tan aplicable para mí.

Jesse Shieh
fuente

Respuestas:

201

No cometas el error de hacer esto:

sh -c "grep ABC {} > {}.out"

Esto se romperá en muchas condiciones, incluidos los nombres de archivos originales y es imposible citarlo correctamente. Su {}siempre debe haber un solo argumento totalmente independiente a la orden de insectos de la inyección de código evitar. Lo que debes hacer es esto:

xargs -I{} sh -c 'grep ABC "$1" > "$1.out"' -- {}

Aplica a xargstan bien como find.

Por cierto, nunca use xargs sin la -0opción (a menos que sea para un uso interactivo muy raro y controlado de una sola vez en el que no le preocupa destruir sus datos).

Tampoco analices ls. Nunca. Use globbing o en su findlugar:http://mywiki.wooledge.org/ParsingLs

Úselo findpara todo lo que necesite recursión y un bucle simple con un globo para todo lo demás:

find /foo -exec sh -c 'grep "$1" > "$1.out"' -- {} \;

o no recursivo:

for file in *; do grep "$file" > "$file.out"; done

Observe el uso apropiado de las comillas.

lhunath
fuente
Votado pero una duda regd. no usar xargssin -0: esto solo se aplica cuando se canaliza findla salida con xargs, ¿verdad? cuando lo haga, ¿ xargs -a <input_file>cómo usaría esto? La mayoría de los comandos tienen gusto de grepsalidas con \ny no. \0.La única forma en que puedo pensar trpara solucionar esto es usar de nuevo para arreglar eso tal vez. Pero, ¿por qué es importante usarlo solo con -0?
legends2k
3
@ legends2k porque cuando no lo uses -0, xargstomará tus nombres de archivo y romperá todos los espacios, comillas y barras invertidas en ellos. Deberías olvidarte xargscomo una herramienta. Si tiene líneas, use un bucle bash para iterar las líneas:, while read line; do <command> "$REPLY"; done < file-with-linesocommand | while ...
lhunath
1
Wow, no sabía sobre eso, ¡gracias por el detalle! Por lo tanto, para la portabilidad (ya que no todos xargsson GNU), xargsdebe evitarse a menos que uno pueda usarlo -0. Gracias.
legends2k
1
Aunque agradezco la explicación detallada de este caso de uso específico, la pregunta es sobre la redirección de la salida de xargs , que no siempre implica el análisis lso el uso sh -c. Esto no responde la pregunta en lo más mínimo, pero es el primer resultado de Google para la pregunta, solo que aumenta la confusión.
pandasauce
1
@Ihunath, hola, tu respuesta me funciona bien. Pero, ¿podría dar alguna explicación detallada o enlaces sobre xargs -I{} sh -c 'grep ABC "$1" > "$1.out"' -- {}? Especialmente, las reglas de comillas (dobles) incrustadas y el símbolo "-" al final. Gracias
Scott Yang
40

Una solución sin xargses la siguiente:

find . -mindepth 1 -maxdepth 1 -type f -exec sh -c "grep ABC '{}' > '{}.out'" \;

... y el mismo se puede hacer con xargs , resulta que:

ls -1 | xargs -I {} sh -c "grep ABC '{}' > '{}.out'"

Editar : comillas simples agregadas después del comentario de lhunath .

Stephan202
fuente
Dijo que quiere usar xargs. Publiqué una solución sin ella también, pero eliminé una vez que vi que necesitaba xargs.
Zifre
Tienes razón. La razón por la que publiqué mi respuesta fue que es mejor tener una solución alternativa para hacer el trabajo que ninguna. Resulta que me puso en el camino correcto para encontrar la respuesta deseada (es decir, el truco sh -c).
Stephan202
14

Supongo que su ejemplo es solo un ejemplo y que puede necesitar> para otras cosas. GNU Parallel http://www.gnu.org/software/parallel/ puede ser su rescate. No necesita comillas adicionales siempre que sus nombres de archivo no contengan \ n:

ls | parallel "grep ABC {} > {}.out"

Si tiene nombres de archivo con \ n:

find . -print0 | parallel -0 "grep ABC {} > {}.out"

Como una ventaja adicional, obtiene los trabajos ejecutados en paralelo.

Mire los videos de introducción para obtener más información: http://pi.dk/1

La instalación de 10 segundos intentará hacer una instalación completa; si eso falla, una instalación personal; si eso falla, una instalación mínima:

$ (wget -O - pi.dk/3 || lynx -source pi.dk/3 || curl pi.dk/3/ || \
   fetch -o - http://pi.dk/3 ) > install.sh
$ sha1sum install.sh | grep 3374ec53bacb199b245af2dda86df6c9
12345678 3374ec53 bacb199b 245af2dd a86df6c9
$ md5sum install.sh | grep 029a9ac06e8b5bc6052eac57b2c3c9ca
029a9ac0 6e8b5bc6 052eac57 b2c3c9ca
$ sha512sum install.sh | grep f517006d9897747bed8a4694b1acba1b
40f53af6 9e20dae5 713ba06c f517006d 9897747b ed8a4694 b1acba1b 1464beb4
60055629 3f2356f3 3e9c4e3c 76e3f3af a9db4b32 bd33322b 975696fc e6b23cfb
$ bash install.sh

Si necesita moverlo a un servidor, que no tiene GNU Parallel instalado, intente parallel --embed.

Ole Tange
fuente