204 votes

Appeler des fonctions shell avec xargs

J'essaie d'utiliser xargs pour appeler une fonction plus complexe en parallèle.

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

Cela renvoie l'erreur

xargs: echo_var: No such file or directory

Toute idée sur la façon dont je peux utiliser xargs pour accomplir ceci, ou toute autre solution serait la bienvenue.

2 votes

Danger, utilisateur1148366, Danger ! N'utilisez pas bash pour la programmation parallèle - vous rencontrerez tellement de problèmes. Utilisez C/C++ et pthreads, ou Java threads, ou n'importe quoi qui vous fasse réfléchir longuement et sérieusement à ce que vous faites, parce que la programmation parallèle demande beaucoup de réflexion pour être correcte.

29 votes

@DavidSouther Si les tâches sont indépendantes, comme convertir tous ces fichiers d'images en png, alors ne vous inquiétez pas. C'est lorsqu'il y a synchronisation (au-delà de l'attente de la fin de toutes les tâches) et communication que les choses se gâtent.

1 votes

@DavidSouther - Je suis un développeur Java de longue date et je travaille en groovy depuis peu. Et je continue à le dire aux gens : Les amis ne laissent pas leurs amis écrire des bash script. Et pourtant, je me retrouve à regarder ce post/solution parce que (visage triste :( ) je suis engagé dans le traitement parallèle dans bash. Je pourrais facilement le faire en groovy/java. Mauvais !

218voto

Dennis Williamson Points 105818

Exporter la fonction devrait le faire (non testé) :

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

Vous pouvez utiliser la fonction intégrée printf au lieu de l'externe seq :

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

De même, l'utilisation de return 0 y exit 0 comme cela masque toute valeur d'erreur qui pourrait être produite par la commande qui la précède. De plus, s'il n'y a pas d'erreur, c'est la valeur par défaut et donc quelque peu redondante.

@phobic mentionne que la commande Bash pourrait être simplifié en

bash -c 'echo_var "{}"'

en déplaçant le {} directement à l'intérieur. Mais es vulnérable à l'injection de commandes comme l'a souligné @Sasha.

Voici un exemple de pourquoi vous ne doit pas utiliser le format intégré :

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

Un autre exemple de pourquoi pas :

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

Voici ce qui est produit utiliser le format sûr :

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

C'est comparable à l'utilisation de paramétré SQL demandes de renseignements pour éviter injection .

J'utilise date dans une substitution de commande ou entre guillemets ici au lieu de l'option rm utilisée dans le commentaire de Sasha car elle est non-destructive.

18voto

Ole Tange Points 4907

L'utilisation de GNU Parallel ressemble à ceci :

#!/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 vous utilisez la version 20170822 vous n'avez même pas besoin de export -f tant que vous avez exécuté ceci :

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

0 votes

Où puis-je obtenir shopt pour osx ?

0 votes

Nvm c'est setopt dans zsh

0 votes

J'obtiens cet eerror Ole ci-dessous sh: parallel_bash_environment: line 67: unexpected EOF while looking for matching '' sh : parallel_bash_environment : line 79 : syntax error : unexpected end of file sh : error importing function definition for 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 : ligne 79 : syntax error : unexpected end of file /usr/local/bin/bash : error importing function definition for ` ...

14voto

Eremite Points 611

Quelque chose comme ça devrait aussi fonctionner :

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

3voto

xdhmoore Points 512

Peut-être que c'est une mauvaise pratique, mais si vous définissez des fonctions dans une .bashrc ou autre script, vous pouvez envelopper le fichier ou au moins les définitions de fonction avec un paramètre 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

3voto

Il semble que je ne puisse pas faire de commentaires :-(

Je me demandais si l'accent était mis sur

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

Le premier substitue les {} comme arg à bash et le second comme arg à la fonction. Le fait que l'exemple 1 ne développe pas le $(date) est simplement un effet secondaire.

Si vous ne voulez pas que les arguments de la fonction soient développés, utilisez des guillemets simples plutôt que doubles. Pour éviter une imbrication désordonnée, utilisez des guillemets doubles (développez les args sur l'autre guillemet).

$ 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)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X