Pregunta

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 funciones init(), ejecutar() y cleanup().Pero no son obligatorios.Me gustaría probar si están definidos o no.

Hice esto anteriormente buscando y codificando la fuente, pero parecía incorrecto.¿Existe una forma más elegante de hacer esto?

Editar:El siguiente fragmento funciona de maravilla:

fn_exists()
{
    LC_ALL=C type $1 | grep -q 'shell function'
}
¿Fue útil?

Solución

Creo que estás buscando el comando 'escribir'.Le dirá si algo es una función, una función incorporada, un comando externo o simplemente no está definido.Ejemplo:

$ 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

Otros consejos

$ g() { return; }
$ declare -f g > /dev/null; echo $?
0
$ declare -f j > /dev/null; echo $?
1

Si declarar es 10 veces más rápido que probar, esta parecería la respuesta obvia.

Editar:Bajo el -f La opción es superflua con BASH, no dudes en omitirla.Personalmente, tengo problemas para recordar qué opción hace cuál, así que uso ambas. -F muestra funciones y -F muestra los nombres de las 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 medible por 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 inútil.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 redirecciones y otros grepping.

Sacando a relucir una publicación antigua...pero recientemente usé esto y probé ambas alternativas descritas con:

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

esto generó:

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

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

¡Declarar es muchísimo más rápido!

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

Estilo de salida:

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

Uso:

isFunction some_name && echo yes || echo no

Sin embargo, si la memoria no me falla, redirigir a nulo es más rápido que la sustitución de salida (hablando de eso, el horrible y obsoleto método `cmd` debería desterrarse y usarse $(cmd) en su lugar). Y dado que declare devuelve verdadero/falso si se encuentra/ no encontrado, y las funciones devuelven el código de salida del último comando de la función, por lo que generalmente 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 esto sea lo más conciso y benigno posible.

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 -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

salidas, por ejemplo:

declarar -f

Real 0M0.037S Usuario 0M0.024S SYS 0M0.012S

declarar -F

Real 0M0.030S Usuario 0M0.020S SYS 0M0.008S

escribe con grep

Real 0M1.772S Usuario 0M0.084S SYS 0M0.340S

escribe con var

Real 0M0.770S Usuario 0M0.096S SYS 0M0.160S

declarar -f (f no establecido)

Real 0M0.031S Usuario 0M0.028S SYS 0M0.000S

declarar -F (f no establecido)

Real 0M0.031S Usuario 0M0.020S SYS 0M0.008S

escriba con grep (f no configurado)

Real 0M1.859S Usuario 0M0.100S SYS 0M0.348S

escriba con var (f no establecido)

Real 0M0.683S Usuario 0M0.092S SYS 0M0.160S

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

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
$

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 Gregorio José

Pero lo modifiqué un poco para superar el "truco feo de las comillas dobles":

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

    if [ "$TYPE_RESULT" == 'function' ]; then
        return 0
    else
        return 1
    fi
}

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

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 'type' sin ningún comando externo, pero hay que llamarlo dos veces, por lo que termina aproximadamente el doble de lento que la versión 'declare':

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 no tiene ningún valor excepto como trivia.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top