208 votes

Déterminer si une fonction existe dans bash

Actuellement, je fais quelques tests unitaires qui sont exécutés à partir de bash. Les tests unitaires sont initialisés, exécutés et nettoyés dans un script bash. Ce script contient habituellement une fonction init(), execute() et cleanup(). Mais elles ne sont pas obligatoires. J'aimerais tester si elles sont ou non définies.

Je l'ai fait précédemment en recherchant et en semant la source, mais cela ne semblait pas correct. Existe-t-il une manière plus élégante de procéder ?

Edit : Le sniplet suivant fonctionne comme un charme :

fn_exists()
{
    type $1 | grep -q 'shell function'
}

0 votes

Merci. Je l'ai utilisé pour définir de manière conditionnelle des versions tronquées de fonctions lors du chargement d'une bibliothèque shell. fn_exists foo || foo() { :; }

2 votes

Vous pouvez sauvegarder le grep en utilisant type -t y == .

0 votes

Ne fonctionne pas lorsque la locale n'est pas l'anglais. type test_function dit test_function on funktio. en cas d'utilisation de la langue finlandaise et ist eine Funktion lorsque vous utilisez l'allemand.

215voto

JBB Points 2013

Je pense que vous cherchez la commande 'type'. Elle vous dira si quelque chose est une fonction, une fonction intégrée, une commande externe, ou simplement non définie. Exemple :

batzel@batzel-laptop:~$ type foo
bash: type: foo: not found
batzel@batzel-laptop:~$ type ls
ls is aliased to `ls --color=auto'
batzel@batzel-laptop:~$ which type
batzel@batzel-laptop:~$ type type
type is a shell builtin

132 votes

type -t $function est le ticket repas.

4 votes

Pourquoi ne pas l'avoir posté comme réponse ? :-)

0 votes

Parce que j'avais posté ma réponse en utilisant declare en premier :-)

96voto

Allan Wind Points 1133
$ g() { return; }
$ declare -f g > /dev/null; echo $?
0
$ declare -f j > /dev/null; echo $?
1

1 votes

A fonctionné à merveille pour moi. D'autant plus que mon shell n'a pas l'option -t pour le type (j'avais beaucoup de problèmes avec le type "$command").

2 votes

En effet, il fonctionne également dans zsh (utile pour les scripts rc), et ne nécessite pas de grep pour le type.

2 votes

@DennisHodapp pas besoin de type -t vous pouvez vous fier au statut de sortie à la place. J'ai longtemps utilisé type program_name > /dev/null 2>&1 && program_name arguments || echo "error" pour voir si je serais capable d'appeler quelque chose. De toute évidence, le type -t et la méthode ci-dessus permet également de détecter le type, et pas seulement s'il est "appelable".

43voto

Orwellophile Points 2695

Si declare est 10x plus rapide que test, cela semble être la réponse évidente.

#!/bin/sh

function_exists() {
    declare -f -F $1 > /dev/null
    return $?
}

function_exists function_name && echo Exists || echo No such function

L'option "-F" de declare permet de retourner uniquement le nom de la fonction trouvée, plutôt que le contenu entier.

Il ne devrait pas y avoir de pénalité de performance mesurable pour l'utilisation de /dev/null, et si cela vous inquiète tant que ça :

fname=`declare -f -F $1`
[ -n "$fname" ]    && echo Declare -f says $fname exists || echo Declare -f says $1 does not exist

Ou combinez les deux, pour votre propre plaisir inutile. Les deux fonctionnent.

fname=`declare -f -F $1`
errorlevel=$?
(( ! errorlevel )) && echo Errorlevel says $1 exists     || echo Errorlevel says $1 does not exist
[ -n "$fname" ]    && echo Declare -f says $fname exists || echo Declare -f says $1 does not exist

2 votes

L'option '-f' est redondante.

3 votes

El -F n'existe pas dans zsh (utile pour la portabilité).

0 votes

-F n'est pas vraiment nécessaire : il semble que cela supprime uniquement la définition/corps de la fonction.

20voto

Grégory Joseph Points 394

En empruntant à d'autres solutions et commentaires, j'ai trouvé ceci :

fn_exists() {
  # appended double quote is an ugly trick to make sure we do get a string -- if $1 is not a known command, type does not output anything
  [ `type -t $1`"" == 'function' ]
}

Utilisé comme ...

if ! fn_exists $FN; then
    echo "Hey, $FN does not exist ! Duh."
    exit 2
fi

Il vérifie si l'argument donné est une fonction, et évite les redirections et autres grepping.

0 votes

Joli, mon préféré du groupe ! Tu ne veux pas de guillemets autour de l'argument aussi ? Comme dans [ $(type -t "$1")"" == 'function' ]

0 votes

Merci @quickshiftin ; je ne sais pas si je veux ces doubles guillemets, mais vous avez probablement raison, bien que une fonction peut-elle même être déclarée avec un nom qui doit être cité ?

5 votes

Vous utilisez bash, utilisez [[...]] au lieu de [...] et se débarrasser de la citation. Il y a aussi la fourchette de backticks, ce qui est lent. Utilisez declare -f $1 > /dev/null à la place.

11voto

delerious010 Points 171

Il s'agit d'un vieux post ... mais j'ai récemment eu recours à cette solution et j'ai testé les deux alternatives décrites avec :

test_declare () {
    a () { echo 'a' ;}

    declare -f a > /dev/null
}

test_type () {
    a () { echo 'a' ;}
    type a | grep -q 'is a function'
}

echo 'declare'
time for i in $(seq 1 1000); do test_declare; done
echo 'type'
time for i in $(seq 1 100); do test_type; done

cela a généré :

real    0m0.064s
user    0m0.040s
sys     0m0.020s
type

real    0m2.769s
user    0m1.620s
sys     0m1.130s

déclarer est sacrément plus rapide !

1 votes

Cela peut être fait sans grep : test_type_nogrep () { a () { echo 'a' ;}; local b=$(type a); c=${b//is a function/}; [ $? = 0 ] && return 1 || return 0; }

0 votes

@qneill J'ai fait un test un peu plus approfondi en ma réponse ,

1 votes

PIPE est l'élément le plus lent. Ce test ne compare pas type y declare . Il compare type | grep con declare . C'est une grande différence.

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