salida redirigir redireccionar redireccionamiento fichero ejemplos comando archivo bash xargs

bash - redirigir - Llamando múltiples comandos a través de xargs



xargs grep (10)

cat a.txt | xargs -I % echo %

En el ejemplo anterior, xargs toma echo % como argumento de comando. Pero en algunos casos, necesito varios comandos para procesar en lugar de uno, por ejemplo:

cat a.txt | xargs -I % {command1; command2; ... }

Pero xargs no acepta esta forma. Una solución que conozco es que puedo definir una función para envolver los comandos, pero no es canalización, no la prefiero. ¿Hay otra solución?



Esta parece ser la versión más segura.

tr ''[/n]'' ''[/0]'' < a.txt | xargs -r0 /bin/bash -c ''command1 "$@"; command2 "$@";''

( -0 puede eliminarse y tr reemplaza con una redirección (o el archivo se puede reemplazar con un archivo separado nulo). Está principalmente allí, ya que principalmente uso xargs con find con salida -print0 ) (Esto también podría ser relevante en las versiones xargs sin la extensión -0 )

Es seguro, ya que args pasará los parámetros al shell como una matriz que lo ejecuta. La shell (al menos bash ) luego los pasaría como una matriz inalterada a los otros procesos cuando todos se obtengan usando ["$@"][1]

Si usas ...| xargs -r0 -I{} bash -c ''f="{}"; command "$f";'' ...| xargs -r0 -I{} bash -c ''f="{}"; command "$f";'' , la asignación fallará si la cadena contiene comillas dobles. Esto es cierto para cada variante utilizando -i o -I . (Debido a que se reemplaza en una cadena, siempre puede inyectar comandos insertando caracteres inesperados (como comillas, comillas invertidas o signos de dólar) en los datos de entrada)

Si los comandos solo pueden tomar un parámetro a la vez:

tr ''[/n]'' ''[/0]'' < a.txt | xargs -r0 -n1 /bin/bash -c ''command1 "$@"; command2 "$@";''

O con algo menos de procesos:

tr ''[/n]'' ''[/0]'' < a.txt | xargs -r0 /bin/bash -c ''for f in "$@"; do command1 "$f"; command2 "$f"; done;''

Si tiene GNU xargs u otro con la extensión -P y desea ejecutar 32 procesos en paralelo, cada uno con no más de 10 parámetros para cada comando:

tr ''[/n]'' ''[/0]'' < a.txt | xargs -r0 -n10 -P32 /bin/bash -c ''command1 "$@"; command2 "$@";''

Esto debería ser robusto contra cualquier carácter especial en la entrada. (Si la entrada está separada por nulo). La versión tr obtendrá alguna entrada no válida si algunas de las líneas contienen nuevas líneas, pero eso es inevitable con un archivo separado por nuevas líneas.


Este es solo otro enfoque sin xargs ni cat:

while read stuff; do command1 "$stuff" command2 "$stuff" ... done < a.txt


Mi actual BKM para esto es

... | xargs -n1 -I % perl -e ''system("echo 1 %"); system("echo 2 %");''

Es desafortunado que esto use perl, que es menos probable que se instale que bash; pero se maneja más entrada que la respuesta aceptada. (Doy la bienvenida a una versión ubicua que no se basa en perl.)

Sugerencia de KeithThompson de

... | xargs -I % sh -c ''command1; command2; ...''

es genial, a menos que tenga el carácter de comentario de shell # en su entrada, en cuyo caso se truncarán parte del primer comando y todo el segundo comando.

Los # de hash pueden ser bastante comunes, si la entrada se deriva de una lista del sistema de archivos, como ls o find, y su editor crea archivos temporales con # en su nombre.

Ejemplo del problema:

$ bash 1366 $> /bin/ls | cat #Makefile# #README# Makefile README

Vaya, aquí está el problema:

$ bash 1367 $> ls | xargs -n1 -I % sh -i -c ''echo 1 %; echo 2 %'' 1 1 1 1 Makefile 2 Makefile 1 README 2 README

Ahh, eso es mejor:

$ bash 1368 $> ls | xargs -n1 -I % perl -e ''system("echo 1 %"); system("echo 2 %");'' 1 #Makefile# 2 #Makefile# 1 #README# 2 #README# 1 Makefile 2 Makefile 1 README 2 README $ bash 1369 $>


Otra posible solución que funciona para mí es algo como:

cat a.txt | xargs bash -c ''command1 $@; command2 $@'' bash

Tenga en cuenta el ''bash'' al final: supongo que se pasa como argv [0] a bash. Sin él, en esta sintaxis, se pierde el primer parámetro de cada comando. Puede ser cualquier palabra.

Ejemplo:

cat a.txt | xargs -n 5 bash -c ''echo -n `date +%Y%m%d-%H%M%S:` ; echo " data: " $@; echo "data again: " $@'' bash


Prefiero el estilo que permite el modo de ejecución en seco (sin | sh ):

cat a.txt | xargs -I % echo "command1; command2; ... " | sh

Funciona con tuberías también:

cat a.txt | xargs -I % echo "echo % | cat " | sh


Puedes usar

cat file.txt | xargs -i sh -c ''command {} | command2 {} && command3 {}''

{} = variable para cada línea en el archivo de texto


Un poco tarde para la fiesta.

Uso el formato a continuación para comprimir mis directorios con miles de archivos pequeños antes de migrar. Si no necesita comillas simples dentro de los comandos, debería funcionar.

Con algunas modificaciones, estoy seguro de que será útil para alguien. Probado en Cygwin (babun)

find . -maxdepth 1 ! -path . -type d -print0 | xargs -0 -I @@ bash -c ''{ tar caf "@@.tar.lzop" "@@" && echo Completed compressing directory "@@" ; }''

find . Encontrar aqui
-maxdepth 1 No entrar en directorios secundarios
! -path . Excluir / Ruta del directorio actual
-type d solo directorios
-print0 Salida separada por bytes nulos / 0
| xargs Tubería xargs a xargs
-0 entrada es nula bytes separados.
-I @@ Placeholder es @@. Reemplace @@ con la entrada.
bash -c ''...'' Ejecutar comando Bash
{...} comando de agrupación
&& Ejecutar el siguiente comando solo si el comando anterior se cerró correctamente (salida 0)

Final Es importante, de lo contrario fallará.

Salida:

Completed compressing directory ./Directory1 with meta characters in it Completed compressing directory ./Directory2 with meta characters in it Completed compressing directory ./Directory3 with meta characters in it

Actualización de julio de 2018:

Si te encantan los hacks y jugar, aquí hay algo interesante:

echo "a b c" > a.txt echo "123" >> a.txt echo "###this is a comment" >> a.txt cat a.txt myCommandWithDifferentQuotes=$(cat <<''EOF'' echo "command 1: $@"; echo ''will you do the fandango?''; echo "command 2: $@"; echo EOF ) < a.txt xargs -I @@ bash -c "$myCommandWithDifferentQuotes" -- @@

Salida:

command 1: a b c will you do the fandango? command 2: a b c command 1: 123 will you do the fandango? command 2: 123 command 1: ###this is a comment will you do the fandango? command 2: ###this is a comment

Explicación:
- Crea un único guión de línea y guárdalo en una variable.
- a.txt lee un a.txt y lo ejecuta como un script bash
- @@ se asegura cada vez que se pasa una línea completa
- Poner @@ después -- asegura que @@ se toma como entrada de parámetro posicional para el comando bash , no como OPTION inicio bash , es decir, como el mismo -c que significa run command

-- es mágico, funciona con muchas otras cosas, es decir, ssh , incluso kubectl


Una cosa que hago es agregar a .bashrc / .profile esta función:

function each() { while read line; do for f in "$@"; do $f $line done done }

entonces puedes hacer cosas como

... | each command1 command2 "command3 has spaces"

que es menos detallado que xargs o -exec. También puede modificar la función para insertar el valor de la lectura en una ubicación arbitraria en los comandos para cada uno, si también necesita ese comportamiento.


cat a.txt | xargs -I % sh -c ''command1; command2; ...''

Tenga en cuenta que este es un uso inútil de gato . Lo escribiría como:

< a.txt xargs -I % sh -c ''command1; command2; ...''

(Sí, la redirección puede ser al principio del comando).

Presumiblemente, el command1 y / o el command2 contendrán uno o más % caracteres; de lo contrario, no tendría mucho sentido la opción -I % para xargs .