目前我正在进行一些从bash执行的单元测试。单元测试在bash脚本中初始化,执行和清理。该脚本通常包含init(),execute()和cleanup()函数。但它们不是强制性的。我想测试它们是否定义。
我之前通过greping和seding来源做了这个,但它似乎错了。有更优雅的方式吗?
编辑:以下代码段就像魅力一样:
fn_exists()
{
LC_ALL=C type $1 | grep -q 'shell function'
}
答案 0 :(得分:173)
我认为你正在寻找'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
答案 1 :(得分:65)
$ g() { return; }
$ declare -f g > /dev/null; echo $?
0
$ declare -f j > /dev/null; echo $?
1
答案 2 :(得分:35)
如果声明比测试快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
答案 3 :(得分:18)
借鉴其他解决方案和评论,我想出了这个:
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。
答案 4 :(得分:9)
挖掘一个旧帖子......但我最近使用了这个并测试了以下描述的两种替代方案:
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!
答案 5 :(得分:6)
归结为使用'declare'检查输出或退出代码。
输出样式:
isFunction() { [[ "$(declare -Ff "$1")" ]]; }
用法:
isFunction some_name && echo yes || echo no
但是,如果内存服务,重定向到null比输出替换更快(说起来,可怕的和过时的`cmd`方法应该被放逐而使用$(cmd)代替。)并且因为declare返回true / false如果找到/未找到,并且函数返回函数中最后一个命令的退出代码,则通常不需要显式返回,并且因为检查错误代码比检查字符串值(甚至是空字符串)更快:
退出状态样式:
isFunction() { declare -Ff "$1" >/dev/null; }
这可能就像你能得到的那样简洁和温和。
答案 6 :(得分:4)
测试不同解决方案的速度
undefined local variable or method `roadname' for #<#<Class:0x00000003a14ef8>:0x007f7ea4628d90>
输出例如:
声明-f
真正的0m0.037s 用户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 unset)
真正的0m0.031s 用户0m0.028s sys 0m0.000s
声明-F(f unset)
真正的0m0.031s 用户0m0.020s sys 0m0.008s
使用grep键入(f unset)
真实0m1.859s 用户0m0.100s sys 0m0.348s
输入var(f unset)
真正的0m0.683s 用户0m0.092s sys 0m0.160s
所以#!/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
似乎是最好的解决方案。
答案 7 :(得分:3)
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
$
答案 8 :(得分:2)
这告诉你它是否存在,但不是它是一个函数
fn_exists()
{
type $1 >/dev/null 2>&1;
}
答案 9 :(得分:2)
我特别喜欢来自Grégory Joseph
的解决方案但我已经修改了一点以克服“双引丑陋技巧”:
function is_executable()
{
typeset TYPE_RESULT="`type -t $1`"
if [ "$TYPE_RESULT" == 'function' ]; then
return 0
else
return 1
fi
}
答案 10 :(得分:2)
从我对另一个答案的评论(当我回到这个页面时我一直不知道)
function val_password($id,$password){
$this->db->select('id,password');
$this->db->from('tbl_st_account');
$this->db->where('id', $id);
$this->db->where('password', $password);
$this->db->limit(1);
$query = $this->db->get();
if($query->num_rows()==1){
return $query->result();
}else{
return false;
}
}
答案 11 :(得分:1)
我会改进它:
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
答案 12 :(得分:0)
可以在没有任何外部命令的情况下使用'type',但是你必须调用它两次,所以它的结尾速度仍然是'declare'版本的两倍:
test_function () {
! type -f $1 >/dev/null 2>&1 && type -t $1 >/dev/null 2>&1
}
另外这在POSIX sh中不起作用,所以除了琐事之外它完全没用!
答案 13 :(得分:0)
已知函数名。假设名称是 my_function
,然后使用
[[ "$(type -t my_function)" == 'function' ]] && my_function;
# or
[[ "$(declare -fF my_function)" ]] && my_function;
变量中的函数名称。如果我们声明func=my_function
,那么我们可以使用
[[ "$(type -t $func)" == 'function' ]] && $func;
# or
[[ "$(declare -fF $func)" ]] && $func;
结果相同的逻辑取反。一次全部。我们在这里使用 ||
而不是 &&
。
[[ "$(type -t my_function)" != 'function' ]] || my_function;
[[ ! "$(declare -fF my_function)" ]] || my_function;
func=my_function
[[ "$(type -t $func)" != 'function' ]] || $func;
[[ ! "$(declare -fF $func)" ]] || $func;
在函数内使用 ||
和 return
的危险情况
以下组合将强制您终止 shell 进程。
# Some script execution strict mode. The essence is in the "-e"
set -euf +x -o pipefail
function run_if_exists(){
my_function=$1
[[ "$(type -t my_function)" != 'function' ]] || return;
$my_function
}
run_if_exists non_existing_function
这是因为上面等价于
set -e
function run_if_exists(){
return 1;
}
run_if_exists
总是会失败。
为了防止这种情况,您必须在函数的这种前提条件中使用 || { true; return; }
或其他东西。
[[ "$(type -t my_function)" != 'function' ]] || { true; return; }