Llamar a funciones de shell con xargs

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.

preguntado el 12 de junio de 12 a las 20:06

Peligro, usuario1148366, ¡Peligro! No use bash para la programación paralela; se encontrará con muchos problemas. Use C/C++ y pthreads, o subprocesos de Java, o cualquier cosa que le haga pensar largo y tendido sobre lo que está haciendo, porque la programación paralela requiere mucha reflexión para hacerlo bien. -

@DavidSouther Si las tareas son independientes, como convertir todos estos archivos de imágenes a png, no se preocupe. Es cuando tienes sincronización (más allá de esperar a que termine todo) y comunicación cuando se complica. -

@DavidSouther: soy un desarrollador de Java desde hace mucho tiempo y últimamente he estado trabajando en Groovy. Y sigo diciéndole a la gente: los amigos no dejan que los amigos escriban bash script. 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. ¡Mal! -

5 Respuestas

Exportar la función debería hacerlo (no probado):

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

Puedes usar el builtin printf en lugar de lo externo seq:

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

Además, usando return 0 and exit 0 así enmascara cualquier valor de error que pueda producir el comando que lo precede. Además, si no hay ningún error, es el valor predeterminado y, por lo tanto, algo redundante.

@phobic menciona que el comando Bash podría ser simplificado a

bash -c 'echo_var "{}"'

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

Aquí hay un ejemplo de por qué Ud. no debería 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 sale usando el formato seguro:

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

Esto es comparable a usar parametrizado SQL consultas para evitar inyección.

Estoy usando date en una sustitución de comando o entre comillas escapadas aquí en lugar de la rm comando utilizado en el comentario de Sasha ya que no es destructivo.

Respondido 18 ago 19, 18:08

Un poco más de discusión: xargs ejecuta una instancia completamente nueva del proceso nombrado. En este caso, usted proporciona 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 child bash, luego se bifurca al subproceso y se ejecuta allí. - david sur

cual es el significado de _ and \ , sin ellos no me funcionaba - Hashbrown

@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 dar por terminado el -exec cláusula en find, pero funciona para mí sin él aquí. De hecho, si la función fuera a utilizar $@ en lugar de $1 entonces vería el punto y coma como parámetro, por lo que debería omitirse. - dennis williamson

El argumento -i para xargs está obsoleto desde entonces. Use -I (i mayúscula) en su lugar. - Nicolás S

Puede simplificar esto incluyendo el argumento de xargs en la cadena de comando para bash con bash -c 'echo_var "{}"'. Entonces no necesita el _ {} al final. - fobia

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 export -f siempre y cuando hayas ejecutado esto:

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

Respondido el 23 de diciembre de 17 a las 23:12

Obtener esto debajo de eerror Ole sh: parallel_bash_environment: line 67: unexpected EOF while looking for matching '' sh: parallel_bash_environment: línea 79: error de sintaxis: fin inesperado del archivo sh: error al importar la definición de 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 del archivo /usr/local/bin/bash: error al importar la definición de la función para ` ... - Nick

Usted ha sido shellbackshock: Shellshock no afectó directamente a GNU Parallel. La solución a shellshock, sin embargo, lo hizo: rompió por completo --env y el truco env_parallel. Se cree que está arreglado en la versión de git: git.savannah.gnu.org/cgit/parallel.git/snapshot/… - Ole Tange

Me gusta esta respuesta, porque me hizo descubrir la herramienta paralela: JR Utilidad

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 @ "

Respondido el 10 de Septiembre de 16 a las 11:09

En general, esto fallaría en los caracteres especiales de shell (p. ej. |, #) e ignore los espacios en blanco en la entrada. En lugar de dejar que bash trate la entrada como código, sugiero dejar que xargs los pase tal cual. echo {1..10} | xargs -n 1 -P4 bash -c "$(declare -f testing);"' testing "$@"; echo "$@";' argv0 - EndlosSchleife

Parece que no puedo hacer comentarios :-(

Me preguntaba sobre el enfoque en

bash -c 'echo_var "$@"' _ {}
vs
bash -c 'echo_var "{}"'

El primero sustituye el {} como argumento para bash mientras que el segundo como argumento para la función. El hecho de que el ejemplo 1 no expanda el $(fecha) es simplemente un efecto secundario.

Si no desea expandir los argumentos de las funciones, use comillas simples simples en lugar de dobles. Para evitar el anidamiento desordenado, use comillas dobles (expanda argumentos en el otro)

$ echo '$(date)' | xargs -0 -L1 -I {} bash -c 'printit "{}"'
Fri 11 Sep 17:02:24 BST 2020

$ echo '$(date)' | xargs -0 -L1 -I {} bash -c "printit '{}'"
$(date)

Respondido el 11 de Septiembre de 20 a las 17:09

Tal vez esta sea una mala práctica, pero si está definiendo funciones en un .bashrc u otra secuencia de comandos, puede envolver 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

Respondido el 02 de diciembre de 17 a las 01:12

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.