Определите, существует ли функция в bash
Вопрос
В настоящее время я выполняю несколько модульных тестов, которые выполняются из bash.Модульные тесты инициализируются, выполняются и очищаются в скрипте bash.Этот скрипт обычно содержит функции init(), execute() и cleanup().Но они не являются обязательными.Я бы хотел проверить, определены ли они или нет.
Я делал это ранее, используя grep и разделяя исходный код, но это казалось неправильным.Есть ли более элегантный способ сделать это?
Редактировать:Следующий фрагмент работает как по волшебству:
fn_exists()
{
LC_ALL=C type $1 | grep -q 'shell function'
}
Решение
Я думаю, вы ищете команду "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
Другие советы
$ g() { return; }
$ declare -f g > /dev/null; echo $?
0
$ declare -f j > /dev/null; echo $?
1
Если declare выполняется в 10 раз быстрее, чем test, это казалось бы очевидным ответом.
Редактировать:Ниже приведен -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
Заимствуя из других решений и комментариев, я пришел к этому:
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
Он проверяет, является ли данный аргумент функцией, и избегает перенаправлений и других действий grepping.
Копаюсь в старом посте ...но недавно я воспользовался этим и протестировал обе альтернативы, описанные в :
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
объявлять - это чертовски быстро !
Это сводится к использованию 'declare' либо для проверки выходных данных, либо для кода выхода.
Стиль вывода:
isFunction() { [[ "$(declare -Ff "$1")" ]]; }
Использование:
isFunction some_name && echo yes || echo no
Однако, если память не подводит, перенаправление на null выполняется быстрее, чем подстановка выходных данных (кстати, ужасный и устаревший метод `cmd` следует исключить и вместо него использовать $(cmd).) И поскольку declare возвращает true / false, если найдено / не найдено, а функции возвращают код выхода последней команды в функции, поэтому явный возврат обычно не требуется, и поскольку проверка кода ошибки выполняется быстрее, чем проверка строкового значения (даже нулевой строки):
Стиль статуса выхода:
isFunction() { declare -Ff "$1" >/dev/null; }
Это, вероятно, самое краткое и безобидное, что вы можете получить.
Скорость тестирования различных решений
#!/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
результаты, например:
объявить -f
реальный 0m0.037s пользователь 0m0.024s система 0m0.012s
объявить -F
реальный 0m0.030s пользователь 0m0.020s система 0m0.008s
введите с помощью grep
реальный 0m1.772s пользователь 0m0.084s система 0m0.340s
введите с помощью var
реальный 0m0.770s пользователь 0m0.096s система 0m0.160s
объявить -f (f не задано)
реальный 0m0.031s пользователь 0m0.028s система 0m0.000s
объявить -F (f не задано)
реальный 0m0.031s пользователь 0m0.020s система 0m0.008s
введите с помощью grep (f не задано)
реальный 0m1.859s пользователь 0m0.100s система 0m0.348s
введите с помощью var (f не задано)
реальный 0m0.683 s пользователь 0m0.092s система 0m0.160s
Итак declare -F f && echo function f exists. || echo function f does not exist.
кажется, это лучшее решение.
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
$
Это говорит вам, существует ли она, но не о том, что это функция
fn_exists()
{
type $1 >/dev/null 2>&1;
}
Мне особенно понравилось решение от Grégory Joseph
Но я немного изменил его, чтобы преодолеть "уродливый трюк с двойными кавычками".:
function is_executable()
{
typeset TYPE_RESULT="`type -t $1`"
if [ "$TYPE_RESULT" == 'function' ]; then
return 0
else
return 1
fi
}
Из моего комментария к другому ответу (который я продолжаю пропускать, когда возвращаюсь на эту страницу)
$ 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
Я бы улучшил его до:
fn_exists()
{
type $1 2>/dev/null | grep -q 'is a function'
}
И используйте это следующим образом:
fn_exists test_function
if [ $? -eq 0 ]; then
echo 'Function exists!'
else
echo 'Function does not exist...'
fi
Можно использовать 'type' без каких-либо внешних команд, но вам придется вызывать его дважды, поэтому он все равно работает примерно в два раза медленнее, чем версия 'declare':
test_function () {
! type -f $1 >/dev/null 2>&1 && type -t $1 >/dev/null 2>&1
}
К тому же это не работает в POSIX sh, так что это совершенно бесполезно, за исключением мелочей!