it-swarm.it

Determina se esiste una funzione in bash

Attualmente sto facendo alcuni test unitari che vengono eseguiti da bash. I test unitari vengono inizializzati, eseguiti e ripuliti in uno script bash. Questo script di solito contiene una init (), execute () e cleanup () funzioni. Ma non sono obbligatori. Mi piacerebbe testare se sono o non sono definiti.

L'ho fatto in precedenza bestemmiando e sedando la fonte, ma sembrava sbagliato. C'è un modo più elegante per farlo?

Modifica: il seguente sniplet funziona come un incantesimo:

fn_exists()
{
    LC_ALL=C type $1 | grep -q 'Shell function'
}
163
terminus

Penso che tu stia cercando il comando 'type'. Ti dirà se qualcosa è una funzione, una funzione integrata, un comando esterno o semplicemente non definito. Esempio:

$ 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
175
JBB
$ g() { return; }
$ declare -f g > /dev/null; echo $?
0
$ declare -f j > /dev/null; echo $?
1
69
Allan Wind

Se dichiarare è 10 volte più veloce del test, questa sembrerebbe la risposta ovvia.

Modifica: sotto, il -f L'opzione è superflua con BASH, sentiti libero di lasciarla fuori. Personalmente, ho difficoltà a ricordare quale opzione fa quale, quindi uso semplicemente entrambi. - f mostra le funzioni e - F mostra i nomi delle funzioni.

#!/bin/sh

function_exists() {
    declare -f -F $1 > /dev/null
    return $?
}

function_exists function_name && echo Exists || echo No such function

L'opzione "-F" per dichiarare fa sì che restituisca solo il nome della funzione trovata, anziché l'intero contenuto.

Non ci dovrebbe essere alcuna penalità di prestazione misurabile per l'uso di/dev/null e se ti preoccupa così tanto:

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

O combina i due, per il tuo divertimento inutile. Entrambi funzionano.

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

Prendendo in prestito da altre soluzioni e commenti, ho pensato a questo:

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

Usato come ...

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

Verifica se l'argomento dato è una funzione ed evita reindirizzamenti e altri grepping.

18
Grégory Joseph

Eliminare un vecchio post ... ma recentemente ne ho fatto uso e ho testato entrambe le alternative descritte 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

questo ha generato:

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

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

dichiarare è un helluvalot più veloce!

9
jonathanserafini

Si riduce a usare 'dichiarare' per controllare il codice di uscita o di uscita.

Stile di uscita:

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

Uso:

isFunction some_name && echo yes || echo no

Tuttavia, se la memoria serve, il reindirizzamento a null è più veloce della sostituzione dell'output (a proposito, il metodo `cmd` terribile e obsoleto dovrebbe essere bandito e dovrebbe essere usato $ (cmd).) E poiché dichiarare restituisce vero/falso se trovato/non trovato e le funzioni restituiscono il codice di uscita dell'ultimo comando nella funzione, pertanto un ritorno esplicito di solito non è necessario e poiché la verifica del codice di errore è più rapida della verifica di un valore di stringa (anche una stringa nulla):

Stile stato di uscita:

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

Questo è probabilmente il più succinto e benigno possibile.

6
Scott

Test di velocità di diverse soluzioni

#!/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

uscite ad es .:

dichiarare -f

reale 0m0.037s utente 0m0.024s sys 0m0.012s

dichiarare -F

reale 0m0.030s utente 0m0.020s sys 0m0.008s

digitare con grep

reale 0m1.772s utente 0m0.084s sys 0m0.340s

digitare con var

reale 0m0.770s utente 0m0.096s sys 0m0.160s

dichiarare -f (f unset)

reale 0m0.031s utente 0m0.028s sys 0m0.000s

dichiarare -F (f unset)

reale 0m0.031s utente 0m0.020s sys 0m0.008s

digitare con grep (f non impostato)

reale 0m1.859s utente 0m0.100s sys 0m0.348s

digitare con var (f unset)

reale 0m0.683s utente 0m0.092s sys 0m0.160s

Così declare -F f && echo function f exists. || echo function f does not exist. sembra essere la soluzione migliore.

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

pdate

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

$ isFunc isFunc
$ echo $?
0
$ isFunc dfgjhgljhk
$ echo $?
1
$ isFunc psgrep && echo yay
yay
$
3
Jonah

Mi è piaciuta particolarmente la soluzione di Grégory Joseph

Ma l'ho modificato un po 'per superare il "brutto trucco della doppia virgoletta":

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

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

Questo ti dice se esiste, ma non che sia una funzione

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

Dal mio commento su un'altra risposta (che mi manca quando torno a questa pagina)

$ 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
2
qneill

Lo migliorerei per:

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

E usalo in questo modo:

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

È possibile utilizzare 'type' senza comandi esterni, ma è necessario chiamarlo due volte, quindi finisce circa due volte più lento della versione 'declare':

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

Inoltre questo non funziona in POSIX sh, quindi è totalmente inutile tranne che per curiosità!

0
Noah Spurrier