Déterminer si une fonction existe dans bash
Question
Actuellement, je fais des 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 généralement les fonctions init (), execute () et cleanup (). Mais ils ne sont pas obligatoires. Je voudrais tester si elles sont ou non définies.
Je l'avais déjà fait auparavant en criant et en semant la source, mais cela semblait faux. Y a-t-il un moyen plus élégant de faire cela?
Modifier: le sniplet suivant fonctionne comme un charme:
fn_exists()
{
LC_ALL=C type $1 | grep -q 'shell function'
}
La solution
Je pense que vous recherchez la commande "type". Il vous dira si quelque chose est une fonction, une fonction intégrée, une commande externe ou tout simplement pas définie. Exemple:
$ LC_ALL=C type foo
bash: type: foo: not found
$ LC_ALL=C type ls
ls is aliased to `ls --color=auto'
$ which type
$ LC_ALL=C type type
type is a shell builtin
$ LC_ALL=C type -t rvm
function
$ if [ -n "$(LC_ALL=C type -t rvm)" ] && [ "$(LC_ALL=C type -t rvm)" = function ]; then echo rvm is a function; else echo rvm is NOT a function; fi
rvm is a function
Autres conseils
$ g() { return; }
$ declare -f g > /dev/null; echo $?
0
$ declare -f j > /dev/null; echo $?
1
Si declare est 10 fois plus rapide que le test, cela semblerait une réponse évidente.
Modifier: ci-dessous, l'option -f
est superflue avec BASH, n'hésitez pas à la laisser. Personnellement, j'ai du mal à me rappeler quelle option fait laquelle, alors je n'utilise que les deux. -f affiche les fonctions et -F les noms de fonctions.
#!/bin/sh
function_exists() {
declare -f -F $1 > /dev/null
return $?
}
function_exists function_name && echo Exists || echo No such function
Le " -F " Cette option permet de ne renvoyer que le nom de la fonction trouvée, plutôt que le contenu entier.
Il ne devrait y avoir aucune pénalité de performance mesurable pour l’utilisation de / dev / null, et si cela vous inquiète beaucoup:
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 plus grand plaisir. Ils travaillent tous les deux.
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
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 greps.
Récupérer un ancien message ... mais j’avais récemment utilisé ce matériel et 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 un helluvalot plus vite!
Cela revient à utiliser 'declare' pour vérifier le code de sortie ou le code de sortie.
Style de sortie:
isFunction() { [[ "$(declare -Ff "$1")" ]]; }
Utilisation:
isFunction some_name && echo yes || echo no
Cependant, si la mémoire est utile, la redirection vers null est plus rapide que la substitution de sortie (en parlant de, la méthode `cmd` obsolète et dépassée devrait être bannie et utilisée à la place $ (cmd)). Et puisque declare renvoie true / false si trouvé / non trouvé, et que les fonctions renvoient le code de sortie de la dernière commande de la fonction, un retour explicite n’est généralement pas nécessaire, et puisque la vérification du code d’erreur est plus rapide que la vérification d’une valeur de chaîne (même une chaîne nulle):
Style de statut de sortie:
isFunction() { declare -Ff "$1" >/dev/null; }
C'est probablement aussi succinct et bénin que vous pouvez obtenir.
Test de la vitesse de différentes solutions
#!/bin/bash
f () {
echo 'This is a test function.'
echo 'This has more than one command.'
return 0
}
test_declare () {
declare -f f > /dev/null
}
test_declare2 () {
declare -F f > /dev/null
}
test_type () {
type -t f | grep -q 'function'
}
test_type2 () {
local var=$(type -t f)
[[ "${var-}" = function ]]
}
post=
for j in 1 2; do
echo
echo 'declare -f' $post
time for i in $(seq 1 1000); do test_declare; done
echo
echo 'declare -F' $post
time for i in $(seq 1 1000); do test_declare2; done
echo
echo 'type with grep' $post
time for i in $(seq 1 1000); do test_type; done
echo
echo 'type with var' $post
time for i in $(seq 1 1000); do test_type2; done
unset -f f
post='(f unset)'
done
affiche par exemple:
déclarer -f
real 0m0.037s utilisateur 0m0.024s sys 0m0.012s
déclarer -F
real 0m0.030s utilisateur 0m0.020s sys 0m0.008s
tapez avec grep
real 0m1.772s utilisateur 0m0.084s sys 0m0.340s
tapez avec var
real 0m0.770s utilisateur 0m0.096s sys 0m0.160s
déclarer -f (f non défini)
real 0m0.031s utilisateur 0m0.028s sys 0m0.000s
déclarer -F (f non défini)
real 0m0.031s utilisateur 0m0.020s sys 0m0.008s
tapez avec grep (f non défini)
real 0m1.859s utilisateur 0m0.100s sys 0m0.348s
tapez avec var (f non défini)
real 0m0.683s utilisateur 0m0.092s sys 0m0.160s
Donc déclarez -F f & amp; & amp; La fonction echo f existe. || La fonction d'écho f n'existe pas.
semble être la meilleure solution.
fn_exists()
{
[[ $(type -t $1) == function ]] && return 0
}
mise à jour
isFunc ()
{
[[ $(type -t $1) == function ]]
}
$ isFunc isFunc
$ echo $?
0
$ isFunc dfgjhgljhk
$ echo $?
1
$ isFunc psgrep && echo yay
yay
$
Ceci vous indique s'il existe, mais pas que ce soit une fonction
fn_exists()
{
type $1 >/dev/null 2>&1;
}
J'ai particulièrement aimé la solution de Gr & # 233; gory Joseph
Mais je l'ai un peu modifié pour surmonter le "double tour laid guillemet":
function is_executable()
{
typeset TYPE_RESULT="`type -t $1`"
if [ "$TYPE_RESULT" == 'function' ]; then
return 0
else
return 1
fi
}
D'après mon commentaire sur une autre réponse (que je manque toujours lorsque je reviens sur cette page)
$ fn_exists() { test x$(type -t $1) = xfunction; }
$ fn_exists func1 && echo yes || echo no
no
$ func1() { echo hi from func1; }
$ func1
hi from func1
$ fn_exists func1 && echo yes || echo no
yes
Je l’améliorerais pour:
fn_exists()
{
type $1 2>/dev/null | grep -q 'is a function'
}
Et utilisez-le comme ceci:
fn_exists test_function
if [ $? -eq 0 ]; then
echo 'Function exists!'
else
echo 'Function does not exist...'
fi
Il est possible d'utiliser 'type' sans commande externe, mais vous devez l'appeler deux fois pour qu'il soit toujours environ deux fois plus lent que la version 'declare':
test_function () {
! type -f $1 >/dev/null 2>&1 && type -t $1 >/dev/null 2>&1
}
De plus, cela ne fonctionne pas dans POSIX sh, il est donc totalement inutile, sauf par hasard!