web-dev-qa-db-fra.com

Bash: passer une fonction en paramètre

Je dois passer une fonction en tant que paramètre dans Bash. Par exemple, le code suivant:

function x() {
  echo "Hello world"
}

function around() {
  echo "before"
  eval $1
  echo "after"
}

around x

Devrait-il produire:

before
Hello world
after

Je sais que eval n'est pas correct dans ce contexte, mais ce n'est qu'un exemple :)

Une idée?

70
cd1

Si vous n'avez besoin de rien de plus sophistiqué que de retarder l'évaluation du nom de la fonction ou de ses arguments, vous n'avez pas besoin de eval:

function x()      { echo "Hello world";          }
function around() { echo before; $1; echo after; }

around x

fait ce que tu veux. Vous pouvez même passer la fonction et ses arguments de cette façon:

function x()      { echo "x(): Passed $1 and $2";  }
function around() { echo before; "$@"; echo after; }

around x 1st 2nd

empreintes

before
x(): Passed 1st and 2nd
after
100
Idelic

Je pense que personne n’a vraiment répondu à la question. Il n'a pas demandé s'il pouvait faire écho aux cordes dans l'ordre. L'auteur de la question veut plutôt savoir s'il peut simuler le comportement du pointeur de fonction.

Il y a quelques réponses qui ressemblent beaucoup à ce que je ferais, et je veux l'élargir avec un autre exemple.

De l'auteur:

function x() {
  echo "Hello world"
}

function around() {
  echo "before"
  ($1)                   <------ Only change
  echo "after"
}

around x

Pour développer cela, nous aurons la fonction x echo "Hello world: $ 1" pour indiquer quand la fonction est réellement exécutée. Nous allons passer une chaîne qui est le nom de la fonction "x":

function x() {
  echo "Hello world:$1"
}

function around() {
  echo "before"
  ($1 HERE)                   <------ Only change
  echo "after"
}

around x

Pour décrire cela, la chaîne "x" est transmise à la fonction around () qui echos "before", appelle la fonction x (via la variable $ 1, premier paramètre transmis à around) en passant l'argument "HERE", enfin .

Autre particularité, il s'agit de la méthodologie d'utilisation des variables en tant que noms de fonctions. Les variables contiennent en fait la chaîne qui est le nom de la fonction et ($ variable arg1 arg2 ...) appelle la fonction en passant les arguments. Voir ci-dessous:

function x(){
    echo $3 $1 $2      <== just rearrange the order of passed params
}

Z="x"        # or just Z=x

($Z  10 20 30)

donne: 30 10 20, où nous avons exécuté la fonction nommée "x" stockée dans la variable Z et passé les paramètres 10 20 et 30.

Ci-dessus, où nous référençons des fonctions en affectant des noms de variable à celles-ci afin que nous puissions utiliser la variable au lieu de connaître réellement le nom de la fonction -sélectionner les appels de fonction que vous allez effectuer en fonction des arguments de la ligne de commande).

En bash, ce ne sont pas des pointeurs de fonction, mais des variables qui font référence à des noms de fonctions que vous utiliserez par la suite.

17
uDude

il n'y a pas besoin d'utiliser eval

function x() {
  echo "Hello world"
}

function around() {
  echo "before"
  var=$($1)
  echo "after $var"
}

around x
16
kurumi

Vous ne pouvez rien transmettre à une fonction autre que des chaînes. Les substitutions de processus peuvent en quelque sorte le simuler. Bash tend à maintenir ouvert le FIFO jusqu'à ce qu'une commande soit développée pour se terminer.

Voici un idiot rapide

foldl() {
    echo $(($(</dev/stdin)$2))
} < <(tr '\n' "$1" <$3)

# Sum 20 random ints from 0-999
foldl + 0 <(while ((n=RANDOM%999,x++<20)); do echo $n; done)

Les fonctions peuvent être exportées, mais ce n'est pas aussi intéressant qu'il y paraît. Je trouve cela principalement utile pour rendre les fonctions de débogage accessibles aux scripts ou à d'autres programmes exécutant des scripts.

(
    id() {
        "$@"
    }

    export -f id
    exec bash -c 'echowrap() { echo "$1"; }; id echowrap hi'
)

id n'obtient toujours qu'une chaîne contenant le nom d'une fonction (importée automatiquement à partir d'une sérialisation dans l'environnement) et ses arguments.

Le commentaire de Pumbaa80 à une autre réponse est également bon (eval $(declare -F "$1")), mais il est principalement utile pour les tableaux, pas pour les fonctions, car ils sont toujours globaux. Si vous deviez exécuter cela dans une fonction, tout ce que cela ferait serait de le redéfinir, de sorte qu'il n'y aurait aucun effet. Il ne peut pas être utilisé pour créer des fermetures, des fonctions partielles ou des "instances de fonctions" en fonction de ce qui est lié à la portée actuelle. Au mieux, cela peut être utilisé pour stocker une définition de fonction dans une chaîne qui est redéfinie ailleurs - mais ces fonctions ne peuvent également être codées en dur que si bien sûr eval est utilisé

En gros, Bash ne peut pas être utilisé comme ça.

5
ormaaj

Une meilleure approche consiste à utiliser des variables locales dans vos fonctions. Le problème devient alors comment obtenez-vous le résultat à l'appelant. Un mécanisme consiste à utiliser la substitution de commande:

function myfunc()
{
    local  myresult='some value'
    echo "$myresult"
}

result=$(myfunc)   # or result=`myfunc`
echo $result

Ici, le résultat est sorti sur la sortie standard et l'appelant utilise la substitution de commande pour capturer la valeur dans une variable. La variable peut ensuite être utilisée au besoin.

2
Anand Thangappan

Vous devriez avoir quelque chose comme:

function around()
{
  echo 'before';
  echo `$1`;
  echo 'after';
}

Vous pouvez alors appeler around x

1
Tim O

eval est probablement le seul moyen de le réaliser. Le seul inconvénient est l’aspect sécurité, car vous devez vous assurer qu’aucun acte malveillant n’est transmis et que seules les fonctions que vous souhaitez appeler soient appelées (avec la vérification qu’il n’a pas de caractères désagréables comme «; en elle aussi).

Donc, si vous appelez le code, eval est probablement le seul moyen de le faire. Notez qu'il existe d'autres formes d'eval qui fonctionneraient probablement aussi avec des sous-commandes ($ () et ``), mais elles ne sont pas plus sûres et sont plus chères.

0
Wes Hardaker