Determinar si existe una función en bash

Actualmente estoy haciendo algunas pruebas unitarias que se ejecutan desde bash. Las pruebas unitarias se inicializan, ejecutan y limpian en un script bash. Este script normalmente contiene una función init (), execute () y cleanup (). Pero no son obligatorios. Me gustaría probar si están o no definidos.

Hice esto previamente greping y seding la fuente, pero parecía estar equivocado. ¿Hay una manera más elegante de hacer esto?

Editar: el siguiente sniplet funciona como un amuleto:

fn_exists() { type $1 | grep -q 'shell function' } 

Creo que estás buscando el comando ‘tipo’. Te dirá si algo es una función, una función incorporada, un comando externo o simplemente no está definido. Ejemplo:

 $ 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 
 $ g() { return; } $ declare -fg > /dev/null; echo $? 0 $ declare -fj > /dev/null; echo $? 1 

Si declarar es 10 veces más rápido que la prueba, esta parece ser la respuesta obvia.

Editar: A continuación, la opción -f es superflua con BASH, no dudes en omitirla. Personalmente, tengo problemas para recordar qué opción hace cuáles, así que solo uso ambas. -f muestra funciones, y -F muestra nombres de funciones.

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

La opción “-F” para declarar hace que solo devuelva el nombre de la función encontrada, en lugar de todo el contenido.

No debería haber ninguna penalización de rendimiento mensurable para usar / dev / null, y si te preocupa tanto:

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

O combine los dos, para su propio disfrute sin sentido. Ambos trabajan.

 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 

Tomando prestado de otras soluciones y comentarios, se me ocurrió esto:

 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' ] } 

Usado como …

 if ! fn_exists $FN; then echo "Hey, $FN does not exist ! Duh." exit 2 fi 

Comprueba si el argumento dado es una función y evita las redirecciones y otras reducciones.

Arrastrando una publicación anterior … pero recientemente tuve el uso de esto y probé las dos alternativas descritas con:

 test_declare () { a () { echo 'a' ;} declare -fa > /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 

esto generó:

 real 0m0.064s user 0m0.040s sys 0m0.020s type real 0m2.769s user 0m1.620s sys 0m1.130s 

declarar es un helluvalot más rápido!

Todo se reduce a usar ‘declarar’ para verificar el código de salida o de salida.

Estilo de salida:

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

Uso:

 isFunction some_name && echo yes || echo no 

Sin embargo, si la memoria sirve, redirigir a nulo es más rápido que la sustitución de salida (hablando de, el método `cmd` horrible y obsoleto debe ser desterrado y $ (cmd) utilizado en su lugar.) Y desde declarar devuelve verdadero / falso si se encuentra / no encontrado, y las funciones devuelven el código de salida del último comando en la función, por lo que normalmente no es necesario un retorno explícito, y dado que verificar el código de error es más rápido que verificar un valor de cadena (incluso una cadena nula):

Estilo de estado de salida:

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

Probablemente sea tan breve y benigno como puedas.

 fn_exists() { [[ $(type -t $1) == function ]] && return 0 } 

actualizar

 isFunc () { [[ $(type -t $1) == function ]] } $ isFunc isFunc $ echo $? 0 $ isFunc dfgjhgljhk $ echo $? 1 $ isFunc psgrep && echo yay yay $ 

Velocidad de prueba de diferentes soluciones

 #!/bin/bash f () { echo 'This is a test function.' echo 'This has more than one command.' return 0 } test_declare () { declare -ff > /dev/null } test_declare2 () { declare -F f > /dev/null } test_type () { type -tf | grep -q 'function' } test_type2 () { local var=$(type -tf) [[ "${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 -ff post='(f unset)' done 

salidas, por ejemplo:

declarar -f

usuario real 0m0.037s 0m0.024s sys 0m0.012s

declarar -F

usuario real 0m0.030s 0m0.020s sys 0m0.008s

escribe con grep

usuario real 0m1.772s 0m0.084s sys 0m0.340s

escribir con var

usuario real 0m0.770s 0m0.096s sys 0m0.160s

declarar -f (f unset)

usuario real 0m0.031s 0m0.028s sys 0m0.000s

declarar -F (f unset)

usuario real 0m0.031s 0m0.020s sys 0m0.008s

escriba con grep (f unset)

usuario real 0m1.859s 0m0.100s sys 0m0.348s

escriba con var (f unset)

usuario real 0m0.683s 0m0.092s sys 0m0.160s

Por lo tanto, declare -F f && echo function f exists. || echo function f does not exist. declare -F f && echo function f exists. || echo function f does not exist. parece ser la mejor solución.

Esto te dice si existe, pero no que sea una función

 fn_exists() { type $1 >/dev/null 2>&1; } 

Me gustó especialmente la solución de Grégory Joseph

Pero lo he modificado un poco para superar el “truco feo de doble cita”:

 function is_executable() { typeset TYPE_RESULT="`type -t $1`" if [ "$TYPE_RESULT" == 'function' ]; then return 0 else return 1 fi } 

Lo mejoraría a:

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

Y úsalo así:

 fn_exists test_function if [ $? -eq 0 ]; then echo 'Function exists!' else echo 'Function does not exist...' fi 

Es posible usar ‘tipo’ sin ningún comando externo, pero tiene que llamarlo dos veces, por lo que aún termina el doble de lento que la versión ‘declarar’:

 test_function () { ! type -f $1 >/dev/null 2>&1 && type -t $1 >/dev/null 2>&1 } 

Además, esto no funciona en POSIX sh, por lo que es totalmente inútil excepto como trivia!

De mi comentario sobre otra respuesta (que sigo perdiendo cuando vuelvo a esta página)

 $ 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