2008-09-17 21 views
127

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.Determine si existe una función en bash

Hice esto previamente greping y seleccionando la fuente, pero me pareció incorrecto. ¿Hay una manera más elegante de hacer esto?

Editar: La siguiente sniplet funciona como un encanto:

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

Gracias. Utilicé esto para definir condicionalmente las versiones de funciones aplazadas al cargar una biblioteca de shell. 'fn_exists foo || foo() {:; } ' – Harvey

Respuesta

144

Creo que usted está 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 
+92

' type -t $ function' es el ticket de comida. –

+3

¿Por qué no lo publicaste como respuesta? :-) – terminus

+0

Porque había publicado mi respuesta usando declare first :-) –

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

funcionó increíble para mí. Especialmente porque mi shell no tiene el distintivo -t para tipo (estaba teniendo muchos problemas con el tipo "$ command") –

+1

De hecho, también funciona en zsh (útil para scripts rc), y no requiere grep para el tipo. – Lloeki

+1

@DennisHodapp no ​​es necesario 'tipo -t', puede confiar en el estado de la salida en su lugar. Hace tiempo que uso 'type nombre_programa>/dev/null 2> & 1 && nombre_programa argumentos || echo "error" 'para ver si podría llamar algo. Obviamente, el 'tipo -t' y el método anterior también permiten detectar el tipo, no solo si es" invocable ". – 0xC0000022L

1

que podrían mejorar a:

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

Y utilizar de esta manera:

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

Esto le indica si existe, pero no que es una función

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

dragado hasta una entrada antigua ... pero recientemente tuve uso de este y probado 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 

esta creación:

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

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

declare es un helluvalot más rápido!

+15

Por supuesto, ya que no estás llamando a grep en la primera. – terminus

+1

Se puede hacer sin grep: 'test_type_nogrep() { a() {echo 'a';}; local b = $ (escriba a); c = $ {b // es una función /}; [$? = 0] && return 1 || return 0; } ' – qneill

+0

@qneill Hice una prueba algo más extensa en [mi respuesta] (http://stackoverflow.com/a/40693218/4414935), – jarno

0

Es posible utilizar 'tipo' sin ningún tipo de comandos externos, pero hay que llamar dos veces, por lo que todavía termina aproximadamente dos veces más lento que el 'declare' versión:

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

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

+0

test_type_nogrep() {a() {echo 'a';}; local b = $ (tipo a); c = $ {b // es una función /}; ps = 0] && return 1 || return 0; } - qneill –

15

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

Se utiliza como ...

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

Se comprueba si el argumento dado es una función, y evita cambios de dirección y otro descremado

+0

¡Agradable, mi favorito del grupo! ¿No quieres dobles comillas alrededor de la discusión también? Como en '[$ (type -t" $ 1 ")" "== 'function']' – quickshiftin

+0

Gracias @quickshiftin; No sé si quiero esas comillas dobles, pero probablemente tengas razón, aunque ... ¿una función incluso puede ser declarada con un nombre que necesita ser citado? –

+0

Buen punto, parece que en este caso no importa. – quickshiftin

2

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

Pero he modificado un poco para superar "cita doble feo truco":

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

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

Si declare es 10 veces más rápido que la prueba, esto parecería la respuesta obvia

Editar: A continuación, la opción -f es superflua con BASH, no dude en dejarlo. Personalmente, tengo problemas para recordar qué opción hace cuáles, así que solo 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 las causas sólo para devolver el nombre de la función encontrado, en lugar de todo el contenido.

No debería haber ninguna penalización en el rendimiento medible para el uso de/dev/null, y si le preocupa mucho:

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

o combinar 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 
+2

La opción '-f' es redundante. – Rajish

+3

La opción '-F' no existe en zsh (útil para la portabilidad) – Lloeki

+0

'-F' tampoco es realmente necesario: parece suprimir la definición/cuerpo de la función solamente. – blueyed

4

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 no sirve, redirigiendo a nula es más rápido que la sustitución de salida (hablando de eso, el horrible y fuera de moda `cmd` el método debe ser desterrado y $ (cmd) usado en su lugar.) Y desde declarar devuelve verdadero/falso si se encuentra/no encuentra, y las funciones devuelven el código de salida del último comando en la función por lo que generalmente no es necesario un retorno explícito, y desde verificar el código de error es más rápido que consultar un valor de cadena e (incluso una cadena nula): estatus estilo

Salir:

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

Eso es probablemente tan breve y benigna que se puede obtener.

+2

Para una máxima concisión use 'isFunction() {declare -F" $ 1 "; }> & - ' – Neil

+0

' isFunction() {declare -F - "$ @">/dev/null; } 'es mi recomendación. También funciona en una lista de nombres (solo tiene éxito si todas son funciones), no da problemas con nombres que comienzan con '-' y, a mi lado (' bash' 4.2.25), 'declare' siempre falla cuando la salida es cerrado con '> & -', porque no puede escribir el nombre en stdout en ese caso – Tino

+0

Y tenga en cuenta que el 'echo' a veces puede fallar con la 'llamada al sistema interrumpida' en algunas plataformas. En ese caso, "check && echo yes || echo no" todavía puede generar 'no' si' check' es verdadero. – Tino

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

actualización

isFunc() 
{ 
    [[ $(type -t $1) == function ]] 
} 

$ isFunc isFunc 
$ echo $? 
0 
$ isFunc dfgjhgljhk 
$ echo $? 
1 
$ isFunc psgrep && echo yay 
yay 
$ 
2

velocidad de ensayo 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 ej .:

declarar -f

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

declare -F

0m0.030s reales usuario 0m0.020s sys 0m0.008s tipo

con grep

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

tipo con var

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

-f declare (f unset)

0m0.031s reales 0m0.028s de usuario sys 0m0.000s

declare -F (f unset)

0m0.031s reales usuario 0m0.020s sys 0m0.008s tipo

con grep (f unset)

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

tipo con var (f establecido) La

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

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

+0

Atención aquí: 'declare -F f' no devuelve un valor distinto de cero si f no existe en zsh, sino bash yes. Ten cuidado de usarlo. 'declare -f f', por otra parte, funciona como se esperaba al unir la definición de la función en stdout (que puede ser molesto ...) –

0

Desde mi comentario sobre otra respuesta (que guardo falta cuando vuelva 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 
Cuestiones relacionadas