Determine if a function exists in bash

Viewed 88761

Currently I'm doing some unit tests which are executed from bash. Unit tests are initialized, executed and cleaned up in a bash script. This script usualy contains an init(), execute() and cleanup() functions. But they are not mandatory. I'd like to test if they are or are not defined.

I did this previously by greping and seding the source, but it seemed wrong. Is there a more elegant way to do this?

Edit: The following sniplet works like a charm:

fn_exists()
{
    LC_ALL=C type $1 | grep -q 'shell function'
}
14 Answers

Like this: [[ $(type -t foo) == function ]] && echo "Foo exists"

The built-in type command will tell you whether something is a function, built-in function, external command, or just not defined.

Additional examples:

$ 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

The builtin bash command declare has an option -F that displays all defined function names. If given name arguments, it will display which of those functions exist, and if all do it will set status accordingly:

$ fn_exists() { declare -F "$1" > /dev/null; }

$ unset f
$ fn_exists f && echo yes || echo no
no

$ f() { return; }
$ fn_exist f && echo yes || echo no
yes

Dredging up an old post ... but I recently had use of this and tested both alternatives described with :

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

this generated :

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

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

declare is a helluvalot faster !

From my comment on another answer (which I keep missing when I come back to this 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

Invocation of a function if defined.

Known function name. Let's say the name is my_function, then use

[[ "$(type -t my_function)" == 'function' ]] && my_function;
# or
[[ "$(declare -fF my_function)" ]] && my_function;

Function's name is stored in a variable. If we declare func=my_function, then we can use

[[ "$(type -t $func)" == 'function' ]] && $func;
# or
[[ "$(declare -fF $func)" ]] && $func;

The same results with || instead of &&
(Such a logic inversion could be useful during coding)

[[ "$(type -t my_function)" != 'function' ]] || my_function;
[[ ! "$(declare -fF my_function)" ]] || my_function;

func=my_function
[[ "$(type -t $func)" != 'function' ]] || $func;
[[ ! "$(declare -fF $func)" ]] || $func;

Strict mode and precondition checks
We have set -e as a strict mode.
We use || return in our function in a precondition.
This forces our shell process to be terminated.

# Set a strict mode for script execution. The essence here is "-e"
set -euf +x -o pipefail

function run_if_exists(){
    my_function=$1

    [[ "$(type -t $my_function)" == 'function' ]] || return;

    $my_function
}

run_if_exists  non_existing_function
echo "you will never reach this code"

The above is an equivalent of

set -e
function run_if_exists(){
    return 1;
}
run_if_exists

which kills your process.
Use || { true; return; } instead of || return; in preconditions to fix this.

    [[ "$(type -t my_function)" == 'function' ]] || { true; return; }
Related