Determine if a function exists in bash

后端 未结 13 2499
再見小時候
再見小時候 2020-11-30 17:39

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(

相关标签:
13条回答
  • 2020-11-30 18:09

    I particularly liked solution from Grégory Joseph

    But I've modified it a little bit to overcome "double quote ugly trick":

    function is_executable()
    {
        typeset TYPE_RESULT="`type -t $1`"
    
        if [ "$TYPE_RESULT" == 'function' ]; then
            return 0
        else
            return 1
        fi
    }
    
    0 讨论(0)
  • 2020-11-30 18:11

    Testing different solutions:

    #!/bin/bash
    
    test_declare () {
        declare -f f > /dev/null
    }
    
    test_declare2 () {
        declare -F f > /dev/null
    }
    
    test_type () {
        type -t f | grep -q 'function'
    }
    
    test_type2 () {
         [[ $(type -t f) = function ]]
    }
    
    funcs=(test_declare test_declare2 test_type test_type2)
    
    test () {
        for i in $(seq 1 1000); do $1; done
    }
    
    f () {
    echo 'This is a test function.'
    echo 'This has more than one command.'
    return 0
    }
    post='(f is function)'
    
    for j in 1 2 3; do
    
        for func in ${funcs[@]}; do
            echo $func $post
            time test $func
            echo exit code $?; echo
        done
    
        case $j in
        1)  unset -f f
            post='(f unset)'
            ;;
        2)  f='string'
            post='(f is string)'
            ;;
        esac
    done
    

    outputs e.g.:

    test_declare (f is function)

    real 0m0,055s user 0m0,041s sys 0m0,004s exit code 0

    test_declare2 (f is function)

    real 0m0,042s user 0m0,022s sys 0m0,017s exit code 0

    test_type (f is function)

    real 0m2,200s user 0m1,619s sys 0m1,008s exit code 0

    test_type2 (f is function)

    real 0m0,746s user 0m0,534s sys 0m0,237s exit code 0

    test_declare (f unset)

    real 0m0,040s user 0m0,029s sys 0m0,010s exit code 1

    test_declare2 (f unset)

    real 0m0,038s user 0m0,038s sys 0m0,000s exit code 1

    test_type (f unset)

    real 0m2,438s user 0m1,678s sys 0m1,045s exit code 1

    test_type2 (f unset)

    real 0m0,805s user 0m0,541s sys 0m0,274s exit code 1

    test_declare (f is string)

    real 0m0,043s user 0m0,034s sys 0m0,007s exit code 1

    test_declare2 (f is string)

    real 0m0,039s user 0m0,035s sys 0m0,003s exit code 1

    test_type (f is string)

    real 0m2,394s user 0m1,679s sys 0m1,035s exit code 1

    test_type2 (f is string)

    real 0m0,851s user 0m0,554s sys 0m0,294s exit code 1

    So declare -F f seems to be the best solution.

    0 讨论(0)
  • 2020-11-30 18:16

    If declare is 10x faster than test, this would seem the obvious answer.

    Edit: Below, the -f option is superfluous with BASH, feel free to leave it out. Personally, I have trouble remembering which option does which, so I just use both. -f shows functions, and -F shows function names.

    #!/bin/sh
    
    function_exists() {
        declare -f -F $1 > /dev/null
        return $?
    }
    
    function_exists function_name && echo Exists || echo No such function
    

    The "-F" option to declare causes it to only return the name of the found function, rather than the entire contents.

    There shouldn't be any measurable performance penalty for using /dev/null, and if it worries you that much:

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

    Or combine the two, for your own pointless enjoyment. They both work.

    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
    
    0 讨论(0)
  • 2020-11-30 18:16

    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 !

    0 讨论(0)
  • 2020-11-30 18:16

    I would improve it to:

    fn_exists()
    {
        type $1 2>/dev/null | grep -q 'is a function'
    }
    

    And use it like this:

    fn_exists test_function
    if [ $? -eq 0 ]; then
        echo 'Function exists!'
    else
        echo 'Function does not exist...'
    fi
    
    0 讨论(0)
  • 2020-11-30 18:17

    It boils down to using 'declare' to either check the output or exit code.

    Output style:

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

    Usage:

    isFunction some_name && echo yes || echo no
    

    However, if memory serves, redirecting to null is faster than output substitution (speaking of, the awful and out-dated `cmd` method should be banished and $(cmd) used instead.) And since declare returns true/false if found/not found, and functions return the exit code of the last command in the function so an explicit return is usually not necessary, and since checking the error code is faster than checking a string value (even a null string):

    Exit status style:

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

    That's probably about as succinct and benign as you can get.

    0 讨论(0)
提交回复
热议问题