21

How can I determine if a function is already defined in a bash script?

I am trying to make my .bash_login script portable between systems, so I want to add logic to only call a function if it exists.

I want to add __git_ps1() to PS1 only if that function exists on that system. This function normally defined in git-completion.bash which comes with git source, or by one of the bash completion scripts that ports/apt installs.

Mateusz Piotrowski
  • 8,029
  • 10
  • 53
  • 79
csexton
  • 24,061
  • 15
  • 54
  • 57

9 Answers9

20

I realize this is an old question, but none of the other answers do this quite as simply as I'd like. This uses type -t (as suggested in a comment by Chen Levy) as an efficient type-of test, but then uses shell string comparison rather than invoking grep.

if [ "$(type -t somefunc)" = 'function' ]; then
    somefunc arg1 arg2
fi

And to take it a step further, it also works indirectly:

funcname=do_it_special_$somehow
if [ "$(type -t $funcname)" != 'function' ]; then
    funcname=do_it_normal
fi
$funcname arg1 arg2
Trevor Robinson
  • 15,694
  • 5
  • 73
  • 72
  • I really like this solution, though feel like the backticks in the string feel awkward. Regardless, I will certainly use this. – csexton Sep 14 '11 at 02:05
  • @csexton You can use `"$(type -t somefunc)"` instead of backticks. – wisbucky Mar 13 '17 at 23:29
  • Do not use this. Depending on the language of the user the word "function" may not exist. – Melroy van den Berg Mar 20 '22 at 18:48
  • @MelroyvandenBerg Citation needed. [Bash docs list specific strings for `-t`](https://www.gnu.org/software/bash/manual/html_node/Bash-Builtins.html#index-type), without any mention of localization. – Trevor Robinson Mar 21 '22 at 19:32
  • Ah you are right. The `-t` should always be provided. This should fix the problem regardless of localization. Meaning this is the only correct way. Or looking at the exit code. – Melroy van den Berg Mar 22 '22 at 20:40
11
if type __git_ps1 | grep -q '^function$' 2>/dev/null; then
    PS1=whatever
fi
chaos
  • 122,029
  • 33
  • 303
  • 309
  • Using `type` was much faster than `declare` -- thanks. – csexton Jun 22 '09 at 13:44
  • 6
    This is almost correct, but it will also return "true" if `__get_ps1` is an alias, builtin, etc. Use `if type -t function_name | grep -q "^function$" ; then ... fi`, to make sure `__git_ps1` is a function. – Chen Levy Apr 05 '11 at 13:41
  • 6
    There is no reason to `grep`; just run `type` which returns true/false if the type exists or not. And, really, it doesn't matter if you use `type -t` or not; if the (standard) `__git_ps1` is defined, then that's probably what you want to use for your PS1 prompt, regardless of how it's defined; i.e., `if type __git_ps1 > /dev/null 2>&1 ; then PS1='\n\w $(__git_ps1)\n\!$ '; fi` – michael Oct 08 '12 at 10:05
  • Do not use this. Depending on the language of the user the word "function" may not exist. – Melroy van den Berg Mar 20 '22 at 18:48
8

You can do it using:

type function_name

in will return your a function definition in case it exists. So you can check whenever output is empty or not.

PS. Even better I've just checked it will output that function is not exist otherwise output function body.

Artem Barger
  • 40,769
  • 9
  • 59
  • 81
6

If you need a /bin/sh compliant version, you can not use typeset or declare to test for a function definition as it is not a shell builtin. Also the -f option to type might not be available on some systems.

The solution I present is partly covered in other answers already:

isFunction() {
  type $1 | head -1 | egrep "^$1.*function\$" >/dev/null 2>&1;
  return;
}

isFunction __git_ps1 && PS1=__git_ps1
marcel
  • 83
  • 1
  • 5
5

declare -F 'function_name' > /dev/null

echo $?

$? result has value 0 if the function exists, 1 otherwise

Fritz G. Mehner
  • 16,550
  • 2
  • 34
  • 41
3

I think it is better to use declare, even if it is slightly slower than type. Type suceeds also for aliases or scripts that are in the PATH.

I am using this:

function function_exists
{
    FUNCTION_NAME=$1

    [ -z "$FUNCTION_NAME" ] && return 1

    declare -F "$FUNCTION_NAME" > /dev/null 2>&1

    return $?
}

So later in my scripts I can easily see what's going on:

if function_exists __git_ps1
then
    PS1=__git_ps1
fi

Or even the still readable one-liner:

function_exists __git_ps1 && PS1=__git_ps1
Jacobo de Vera
  • 1,863
  • 1
  • 16
  • 20
1

You may list all available functions or check individual functions with compgen:

help compgen

compgen -A function

compgen -A function myfunc 1>/dev/null && echo 'myfunc already exists' || exit 1
tim
  • 11
  • 1
0
if declare -F | grep __git_ps1$
then
    PS1=whatever
fi
Jaime Soriano
  • 7,309
  • 2
  • 33
  • 45
0

Note, busybox (minimalist all in one shell implementation, useful on Windows for example) has "type" but "type -t" prints the wrong thing, so I just check the return value of type to see if something is callable. busybox also does not have declare.

Reed Hedges
  • 1,590
  • 2
  • 15
  • 17