it-swarm-pt.tech

Determinar se existe uma função no bash

Atualmente estou fazendo alguns testes de unidade que são executados a partir do bash. Os testes de unidade são inicializados, executados e limpos em um script bash. Esse script geralmente contém as funções init (), execute () e cleanup (). Mas eles não são obrigatórios. Eu gostaria de testar se eles estão ou não definidos.

Eu fiz isso anteriormente, contornando e seduzindo a fonte, mas parecia errado. Existe uma maneira mais elegante de fazer isso?

Edit: O sniplet a seguir funciona como um encanto:

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

Eu acho que você está procurando o comando 'type'. Ele informa se algo é uma função, função interna, comando externo ou simplesmente não definido. Exemplo:

$ 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 declarar é 10x mais rápido que o teste, esta parece a resposta óbvia.

Edit: Abaixo, a opção -f é supérflua com o BASH, fique à vontade para deixar de fora. Pessoalmente, tenho dificuldade em lembrar qual opção faz qual, então apenas uso as duas. - f mostra funções e - F mostra nomes de funções.

#!/bin/sh

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

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

A opção "-F" a declarar faz com que ele retorne apenas o nome da função encontrada, em vez de todo o conteúdo.

Não deve haver nenhuma penalidade de desempenho mensurável pelo uso de/dev/null e se isso lhe preocupa tanto:

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

Ou combine os dois, para seu próprio prazer inútil. Ambos trabalham.

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

Tomando emprestado de outras soluções e comentários, eu vim com isso:

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

Ele verifica se o argumento fornecido é uma função e evita redirecionamentos e outros grepping.

18
Grégory Joseph

Dragagem de um post antigo ... mas recentemente eu usei isso e testei as duas alternativas descritas com:

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

isso gerou:

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

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

declarar é muito mais rápido!

9
jonathanserafini

Tudo se resume a usar 'declare' para verificar o código de saída ou saída.

Estilo de saída:

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

Uso:

isFunction some_name && echo yes || echo no

No entanto, se a memória servir, o redirecionamento para nulo é mais rápido que a substituição da saída (falando sobre, o péssimo e desatualizado método `cmd` deve ser banido e $ (cmd) usado em seu lugar.) E como declare retorna true/false se encontrado/não encontrado, e funções retornam o código de saída do último comando na função, portanto, um retorno explícito geralmente não é necessário e, uma vez que a verificação do código de erro é mais rápida do que a verificação de um valor de sequência (mesmo uma sequência nula):

Estilo de status de saída:

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

Provavelmente é o mais sucinto e benigno possível.

6
Scott

Testando a velocidade de diferentes soluções

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

saídas, por exemplo:

declare -f

usuário 0m0.037s real 0m0.024s sys 0m0.012s

declarar -F

0m0.030s real usuário 0m0.020s sys 0m0.008s

digite com grep

usuário 0m1.772s real 0m0.084s sys 0m0.340s

digite com var

usuário 0m0.770s real 0m0.096s sys 0m0.160s

declarar -f (f desativado)

usuário 0m0.031s real 0m0.028s sys 0m0.000s

declarar -F (f desativado)

usuário 0m0.031s real 0m0.020s sys 0m0.008s

digite com grep (f desativado)

usuário 0m1.859s real 0m0.100s sys 0m0.348s

tipo com var (f desativado)

usuário 0m0.683s real 0m0.092s sys 0m0.160s

Então declare -F f && echo function f exists. || echo function f does not exist. parece ser a melhor solução.

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

atualização

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

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

Eu particularmente gostei da solução de Grégory Joseph

Mas eu o modifiquei um pouco para superar o "truque feio de aspas duplas":

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

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

Isso informa se existe, mas não é uma função

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

Do meu comentário sobre outra resposta (que continuo faltando quando volto 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
2
qneill

Eu o melhoraria para:

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

E use-o assim:

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

É possível usar 'type' sem nenhum comando externo, mas você deve chamá-lo duas vezes, para que ainda termine duas vezes mais lento que a versão 'declare':

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

Além disso, isso não funciona no POSIX sh, por isso é totalmente inútil, exceto como trivialidades!

0
Noah Spurrier