Déterminer si une fonction existe dans bash

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 habituellement les fonctions init(), execute() et cleanup (). Mais ils ne sont pas obligatoires. Je voudrais tester si elles sont ou ne sont pas définis.

Je l'ai fait précédemment en greping et seding la source, mais cela semblait faux. Est-il une façon plus élégante de le faire?

Edit: le sniplet suivant fonctionne comme un charme:

fn_exists()
{
    type $1 | grep -q 'shell function'
}
139
demandé sur Chris Upchurch 2008-09-17 21:57:38

13 réponses

Je pense que vous cherchez 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:

$ type foo
bash: type: foo: not found

$ type ls
ls is aliased to `ls --color=auto'

$ which type

$ type type
type is a shell builtin

$ type -t rvm
function

$ if [ -n "$(type -t rvm)" ] && [ "$(type -t rvm)" = function ]; then echo rvm is a function; else echo rvm is NOT a function; fi
rvm is a function
157
répondu JBB 2015-04-15 23:44:30
$ g() { return; }
$ declare -f g > /dev/null; echo $?
0
$ declare -f j > /dev/null; echo $?
1
56
répondu Allan Wind 2017-01-10 10:52:57

Si declare est 10 fois plus rapide que test, cela semblerait la réponse évidente.

Edit: ci-dessous, l'option -f est superflue avec BASH, n'hésitez pas à la laisser de côté. Personnellement, j'ai du mal à me rappeler quelle option fait, donc j'utilise juste les deux. -f montre que les fonctions et -F indique les noms des fonctions.

#!/bin/sh

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

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

L'option" - F " à déclarer l'amène à ne renvoyer que le nom de la fonction trouvée, plutôt que le contenu entier.

Voilà ne devrait pas être une pénalité de performance mesurable pour l'utilisation de/dev / null, et si cela vous inquiète autant:

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. 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
32
répondu Orwellophile 2016-12-01 21:08:05

Empruntant à d'autres solutions et commentaires, je suis venu avec 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.

16
répondu Grégory Joseph 2012-01-25 11:34:09

Draguer un vieux poste ... mais j'ai récemment eu recours à cela 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 rapide !

8
répondu jonathanserafini 2010-02-12 15:57:48

Cela se résume à utiliser 'declare' pour vérifier le code de sortie ou de sortie.

Style de sortie:

isFunction() { [[ "$(declare -Ff "$1")" ]]; }

Utilisation:

isFunction some_name && echo yes || echo no

Cependant, si la mémoire sert, la redirection vers null est plus rapide que la substitution de sortie(en parlant de, la méthode `cmd` horrible et obsolète devrait être bannie et $(cmd) utilisée à la place.) Et puisque declare renvoie true / false si trouvé / non trouvé, et les fonctions renvoient le code de sortie de la dernière commande de la fonction, donc 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 D'État de sortie:

isFunction() { declare -Ff "$1" >/dev/null; }

C'est probablement aussi succinct et bénin que possible.

6
répondu Scott 2012-05-28 18:09:26
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
$
3
répondu Jonas 2016-07-13 07:27:47

Vitesse D'essai 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

Sorties par exemple:

Declare-f

Réel 0m0. 037s user 0m0.024s sys 0m0. 012s

Declare-F

Réel 0m0. 030s user 0m0.020s sys 0m0. 008s

Type avec grep

Réel 0m1. 772s user 0m0.084s sys 0m0. 340s

Tapez avec var

Réel 0m0. 770s user 0m0.096s sys 0m0. 160s

Declare-f (F unset)

Réel 0m0. 031s user 0m0.028s sys 0m0. 000s

Declare-F (F unset)

Réel 0m0. 031s user 0m0.020s sys 0m0. 008s

Type avec grep (F unset)

Réel 0m1. 859s user 0m0.100s sys 0m0. 348s

Tapez avec var (F unset)

Réel 0m0. 683s user 0m0.092s sys 0m0. 160s

Donc, declare -F f && echo function f exists. || echo function f does not exist. semble être la meilleure solution.

3
répondu jarno 2017-04-17 08:37:59

Cela vous indique si elle existe, mais pas que c'est une fonction

fn_exists()
{
  type $1 >/dev/null 2>&1;
}
2
répondu Jason Plank 2011-11-22 18:40:12

J'ai particulièrement aimé la solution de Grégory Joseph

Mais je l'ai un peu modifié pour surmonter "double citation ugly trick":

function is_executable()
{
    typeset TYPE_RESULT="`type -t $1`"

    if [ "$TYPE_RESULT" == 'function' ]; then
        return 0
    else
        return 1
    fi
}
2
répondu b1r3k 2017-05-23 11:47:26

Je l'améliorerais à:

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
1
répondu 2009-10-08 22:18:08

Il est possible d'utiliser 'type' sans aucune commande externe, mais vous devez l'appeler deux fois, donc il finit 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, donc c'est totalement sans valeur sauf comme trivia!

0
répondu Noah Spurrier 2010-06-25 21:09:36

De mon commentaire sur une autre réponse (que je continue à manquer quand je reviens à 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
0
répondu qneill 2017-10-11 14:34:54