Llamar a funciones de shell con xargs


168

Estoy tratando de usar xargs para llamar a una función más compleja en paralelo.

#!/bin/bash
echo_var(){
    echo $1
    return 0
}
seq -f "n%04g" 1 100 |xargs -n 1 -P 10 -i echo_var {} 
exit 0

Esto devuelve el error

xargs: echo_var: No such file or directory

Cualquier idea sobre cómo puedo usar xargs para lograr esto, o cualquier otra solución (s) sería bienvenida.


2
¡Peligro, usuario 1148366, peligro! No use bash para programación paralela, se encontrará con muchos problemas. Utilice C / C ++ y pthreads, o hilos de Java, o cualquier cosa que lo haga pensar mucho sobre lo que está haciendo, porque la programación paralela requiere mucho pensamiento para hacerlo bien.
David Souther

27
@DavidSouther Si las tareas son independientes, como convertir todos estos archivos de imagen a png, no se preocupe. Es cuando tienes sincronización (más allá de esperar a que todo termine) y comunicación que se vuelve desordenado.
ctrl-alt-delor

@DavidSouther: soy un desarrollador de Java desde hace mucho tiempo y últimamente he estado trabajando muy bien. Y sigo diciéndole a la gente: los amigos no dejan que los amigos escriban un script bash. Y, sin embargo, me encuentro mirando esta publicación / solución porque (cara triste :() Estoy involucrado en el procesamiento paralelo en bash. Podría hacerlo fácilmente en groovy / java. ¡Malo!
Christian Bongiorno

Respuestas:


172

La exportación de la función debería hacerlo (sin probar):

export -f echo_var
seq -f "n%04g" 1 100 | xargs -n 1 -P 10 -I {} bash -c 'echo_var "$@"' _ {}

Puede usar el incorporado en printflugar del externo seq:

printf "n%04g\n" {1..100} | xargs -n 1 -P 10 -I {} bash -c 'echo_var "$@"' _ {}

Además, el uso return 0y exit 0similares oculta cualquier valor de error que pueda generar el comando que lo precede. Además, si no hay error, es el valor predeterminado y, por lo tanto, algo redundante.

@phobic menciona que el comando Bash podría simplificarse para

bash -c 'echo_var "{}"'

moviendo el {}directamente dentro de él. Pero es vulnerable a la inyección de comandos como lo señaló @Sasha.

Aquí hay un ejemplo de por qué no debe usar el formato incrustado:

$ echo '$(date)' | xargs -I {} bash -c 'echo_var "{}"'
Sun Aug 18 11:56:45 CDT 2019

Otro ejemplo de por qué no :

echo '\"; date\"' | xargs -I {} bash -c 'echo_var "{}"'

Esto es lo que se genera con el formato seguro :

$ echo '$(date)' | xargs -I {} bash -c 'echo_var "$@"' _ {}
$(date)

Esto es comparable al uso de consultas SQL parametrizadas para evitar la inyección .

Estoy usando dateuna sustitución de comando o entre comillas escapadas aquí en lugar del rmcomando utilizado en el comentario de Sasha, ya que no es destructivo.


14
Un poco más de discusión: xargs ejecuta una instancia completamente nueva del proceso llamado. En este caso, debe proporcionar el nombre echo_var, que es una función en este script, no un proceso (programa) en su RUTA. Lo que hace la solución de Dennis es exportar la función para que la usen los procesos de bash secundarios, luego se bifurca al subproceso y se ejecuta allí.
David Souther

77
cuál es el significado _y \, sin ellos, no funcionaba para mí
Hashbrown

9
@Hashbrown: El guión bajo ( _) proporciona un marcador de posición para argv[0]( $0) y casi cualquier cosa podría usarse allí. Creo que agregué la barra invertida-punto y coma ( \;) debido a su uso para terminar la -execcláusula find, pero funciona para mí sin ella aquí. De hecho, si la función se usara en $@lugar de $1entonces, vería el punto y coma como un parámetro, por lo que debería omitirse.
Pausado hasta nuevo aviso.

44
-i argumento para xargs ya no se utiliza. Use -I (mayúscula i) en su lugar.
Nicolai S

11
Puede simplificar esto incluyendo el argumento de xargs en la cadena de comando para bash with bash -c 'echo_var "{}"'. Por lo tanto, no necesita el _ {} al final.
fóbico

16

El uso de GNU Parallel se ve así:

#!/bin/bash
echo_var(){
    echo $1
    return 0
}
export -f echo_var
seq -f "n%04g" 1 100 | parallel -P 10 echo_var {} 
exit 0

Si usa la versión 20170822, ni siquiera tiene que hacerlo export -fsiempre que haya ejecutado esto:

. `which env_parallel.bash`
seq -f "n%04g" 1 100 | env_parallel -P 10 echo_var {} 

¿Dónde consigo shopt para osx?
Nick

nvm setopt en zsh
Nick

Obtener esto a continuación eerror Ole sh: parallel_bash_environment: line 67: unexpected EOF while looking for matching '' sh: paralela_bash_environment: línea 79: error de sintaxis: final inesperado del archivo sh: error al importar la definición de la función para parallel_bash_environment' /usr/local/bin/bash: parallel_bash_environment: line 67: unexpected EOF while looking for matching '' / usr / local / bin / bash: parallel_bash_environment: línea 79: error de sintaxis: final inesperado de file / usr / local / bin / bash: error al importar la definición de la función para `...
Nick

Has recibido un shellaftershocked: Shellshock no afectó a GNU Parallel directamente Sin embargo, la solución al shellshock sí lo hizo: rompió por completo --env y el truco env_parallel. Se cree que está solucionado en la versión git: git.savannah.gnu.org/cgit/parallel.git/snapshot/…
Ole Tange

1
Me gusta esta respuesta, porque me hizo descubrir la herramienta paralela
JR Utily

10

Algo como esto debería funcionar también:

function testing() { sleep $1 ; }
echo {1..10} | xargs -n 1 | xargs -I@ -P4 bash -c "$(declare -f testing) ; testing @ ; echo @ "

1

Tal vez esta sea una mala práctica, pero si está definiendo funciones en una .bashrcu otra secuencia de comandos, puede ajustar el archivo o al menos las definiciones de función con una configuración de allexport:

set -o allexport

function funcy_town {
  echo 'this is a function'
}
function func_rock {
  echo 'this is a function, but different'
}
function cyber_func {
  echo 'this function does important things'
}
function the_man_from_funcle {
  echo 'not gonna lie'
}
function funcle_wiggly {
  echo 'at this point I\'m doing it for the funny names'
}
function extreme_function {
  echo 'goodbye'
}

set +o allexport
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.