Определите, существует ли функция в bash
В настоящее время я делаю некоторые модульные тесты, которые выполняются из Bash. Модульные тесты инициализируются, выполняются и очищаются в bash-скрипте. Этот скрипт обычно содержит функции init(), execute() и cleanup(). Но они не обязательны. Я хотел бы проверить, определены они или нет.
Раньше я делал это с помощью поиска и подмены источника, но это казалось неправильным. Есть ли более элегантный способ сделать это?
Изменение: следующий фрагмент работает как шарм:
fn_exists()
{
LC_ALL=C type $1 | grep -q 'shell function'
}
Ответы
Ответ 1
Я думаю, что вы ищете команду типа. Он скажет вам, является ли что-то функцией, встроенной функцией, внешней командой или просто не определено. Пример:
$ 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
Ответ 2
$ g() { return; }
$ declare -f g > /dev/null; echo $?
0
$ declare -f j > /dev/null; echo $?
1
Ответ 3
Если declare будет в 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
Ответ 4
Заимствуя другие решения и комментарии, я придумал следующее:
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.
Ответ 5
Дрессировка старого сообщения... но я недавно использовал это и испытал обе альтернативы, описанные с помощью:
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
Объявление - это helluvalot быстрее!
Ответ 6
Это сводится к использованию 'declare' для проверки кода выхода или выхода.
Стиль вывода:
isFunction() { [[ "$(declare -Ff "$1")" ]]; }
Использование:
isFunction some_name && echo yes || echo no
Однако, если память обслуживается, перенаправление на нуль быстрее, чем замена вывода (говоря, ужасный и устаревший метод `cmd` должен быть изгнан и вместо него используется $(cmd).) И поскольку declare возвращает true/false если обнаружено/не найдено, а функции возвращают код завершения последней команды в функции, поэтому явный возврат обычно не требуется, и поскольку проверка кода ошибки выполняется быстрее, чем проверка строкового значения (даже пустая строка):
Стиль статуса выхода:
isFunction() { declare -Ff "$1" >/dev/null; }
Это, вероятно, примерно так же кратким и мягким, как вы можете получить.
Ответ 7
Тестирование различных решений:
#!/bin/bash
test_declare () {
declare -f f > /dev/null
}
test_declare2 () {
declare -F f > /dev/null
}
test_type () {
type -t f | grep -q 'function'
}
test_type2 () {
[[ $(type -t f) = function ]]
}
funcs=(test_declare test_declare2 test_type test_type2)
test () {
for i in $(seq 1 1000); do $1; done
}
f () {
echo 'This is a test function.'
echo 'This has more than one command.'
return 0
}
post='(f is function)'
for j in 1 2 3; do
for func in ${funcs[@]}; do
echo $func $post
time test $func
echo exit code $?; echo
done
case $j in
1) unset -f f
post='(f unset)'
;;
2) f='string'
post='(f is string)'
;;
esac
done
выводит, например:
test_declare (f is function)
реальный 0m0,055s пользователь 0m0,041s sys 0m0,004s код выхода 0
test_declare2 (f is function)
реальный 0m0,042s пользователь 0m0,022s sys 0m0,017s код выхода 0
test_type (f is function)
реальный 0m2,200s пользователь 0m1,619s sys 0m1,008s код выхода 0
test_type2 (f is function)
реальный 0m0,746s пользователь 0m0,534s sys 0m0,237s код выхода 0
test_declare (f unset)
реальный 0m0,040s пользователь 0m0,029s sys 0m0,010s код выхода 1
test_declare2 (f unset)
реальный 0m0,038s пользователь 0m0,038s sys 0m0,000s код выхода 1
test_type (f unset)
реальный 0m2,438s пользователь 0m1,678s sys 0m1,045s код выхода 1
test_type2 (f unset)
реальный 0m0,805s пользователь 0m0,541s sys 0m0,274s код выхода 1
test_declare (f is string)
реальный 0m0,043s пользователь 0m0,034s sys 0m0,007s код выхода 1
test_declare2 (f is string)
реальный 0m0,039s пользователь 0m0,035s sys 0m0,003s код выхода 1
test_type (f is string)
реальный 0m2,394s пользователь 0m1,679s sys 0m1,035s код выхода 1
test_type2 (f is string)
реальный 0m0,851s пользователь 0m0,554s sys 0m0,294s код выхода 1
Так что declare -F f
кажется лучшим решением.
Ответ 8
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
$
Ответ 9
Из моего комментария к другому ответу (который я не вижу, когда вернусь на эту страницу)
$ 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
Ответ 10
Это говорит вам, существует ли он, но не то, что это функция
fn_exists()
{
type $1 >/dev/null 2>&1;
}
Ответ 11
Мне особенно понравилось решение от Grégory Joseph
Но я немного изменил его, чтобы преодолеть "двойную цитату уродливого трюка":
function is_executable()
{
typeset TYPE_RESULT="`type -t $1`"
if [ "$TYPE_RESULT" == 'function' ]; then
return 0
else
return 1
fi
}
Ответ 12
Я бы улучшил его, чтобы:
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
Ответ 13
Можно использовать "тип" без каких-либо внешних команд, но вы должны называть его дважды, поэтому он все равно заканчивается примерно в два раза медленнее, чем версия "declare":
test_function () {
! type -f $1 >/dev/null 2>&1 && type -t $1 >/dev/null 2>&1
}
Плюс это не работает в POSIX sh, поэтому он абсолютно бесполезен, кроме как мелочи!