Определить, существует ли функция в bash - PullRequest
160 голосов
/ 17 сентября 2008

В настоящее время я делаю некоторые модульные тесты, которые выполняются из bash. Модульные тесты инициализируются, выполняются и очищаются в bash-скрипте. Этот скрипт обычно содержит функции init (), execute () и cleanup (). Но они не обязательны. Я хотел бы проверить, определены они или нет.

Раньше я делал это, подбирая и подсеивая источник, но это казалось неправильным. Есть ли более элегантный способ сделать это?

Редактировать: следующий фрагмент работает как шарм:

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

Ответы [ 13 ]

173 голосов
/ 17 сентября 2008

Я думаю, вы ищете команду 'type'. Он скажет вам, является ли что-то функцией, встроенной функцией, внешней командой или просто не определено. Пример:

$ 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
65 голосов
/ 17 сентября 2008
$ g() { return; }
$ declare -f g > /dev/null; echo $?
0
$ declare -f j > /dev/null; echo $?
1
35 голосов
/ 02 марта 2012

Если объявление в 10 раз быстрее, чем тестирование, это может показаться очевидным ответом.

Редактировать: ниже, опция -f излишня с BASH, не стесняйтесь опускать ее. Лично у меня проблемы с запоминанием того, какая опция делает, поэтому я просто использую оба. -f показывает функции, а -F показывает имена функций.

#!/bin/sh

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

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

Опция "-F" для объявления заставляет его возвращать только имя найденной функции, а не все содержимое.

Не должно быть никакого измеримого снижения производительности при использовании / dev / null, и если это вас так сильно беспокоит:

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

Или объедините их для собственного бессмысленного удовольствия. Они оба работают.

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
18 голосов
/ 25 января 2012

Заимствуя из других решений и комментариев, я придумал это:

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

Используется как ...

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

Он проверяет, является ли данный аргумент функцией, и избегает перенаправлений и других ошибок.

9 голосов
/ 12 февраля 2010

Выкапывание старого сообщения ... но я недавно использовал это и проверил обе альтернативы, описанные с помощью:

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

это сгенерировано:

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

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

объявляй, черт возьми, быстрее!

6 голосов
/ 28 мая 2012

Это сводится к тому, чтобы использовать 'Declare' для проверки кода выхода или выхода.

Стиль вывода:

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

Использование:

isFunction some_name && echo yes || echo no

Однако, если память служит, перенаправление на ноль происходит быстрее, чем подстановка вывода (если говорить о том, что ужасный и устаревший метод `cmd` должен быть изгнан, а вместо него должен использоваться $ (cmd).) И так как метод Declare возвращает true / false если найдено / не найдено, и функции возвращают код завершения последней команды в функции, поэтому явный возврат обычно не требуется, и поскольку проверка кода ошибки выполняется быстрее, чем проверка строкового значения (даже нулевой строки):

Стиль статуса выхода:

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

Это, вероятно, настолько кратко и мягко, насколько вы можете получить.

4 голосов
/ 19 ноября 2016

Скорость тестирования различных решений

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

выходы, например ::1004 *

объявить -f

реальное 0m0,037 с пользователь 0m0.024s sys 0m0,012s

объявить -F

реальный 0m0.030s пользователь 0m0.020s sys 0m0.008s

тип с grep

реальный 0m1,772s пользователь 0m0.084s sys 0m0,340s

тип с VAR

реальный 0m0,770s пользователь 0m0.096s sys 0m0,160s

объявить -f (f не установлено)

реальное 0m0,031 с пользователь 0m0.028s sys 0m0.000s

объявить -F (f не установлено)

реальный 0m0,031s пользователь 0m0.020s sys 0m0.008s

тип с grep (f не установлен)

реальный 0m1.859s пользователь 0m0.100s sys 0m0,348s

тип с переменной var (f unset)

реальный 0m0.683s пользователь 0m0.092s sys 0m0,160s

Так что declare -F f && echo function f exists. || echo function f does not exist. кажется лучшим решением.

3 голосов
/ 31 января 2016
fn_exists()
{
   [[ $(type -t $1) == function ]] && return 0
}

обновление

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

$ isFunc isFunc
$ echo $?
0
$ isFunc dfgjhgljhk
$ echo $?
1
$ isFunc psgrep && echo yay
yay
$
2 голосов
/ 11 октября 2017

Из моего комментария к другому ответу (который я постоянно пропускаю, когда возвращаюсь на эту страницу)

$ 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 голосов
/ 27 февраля 2012

Мне особенно понравилось решение от Грегори Жозеф

Но я немного изменил его, чтобы преодолеть «уродливый трюк с двойной кавычкой»:

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

    if [ "$TYPE_RESULT" == 'function' ]; then
        return 0
    else
        return 1
    fi
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...