确定bash中是否存在函数

确定bash中是否存在函数

Determine if a function exists in bash

目前,我正在做一些从bash执行的单元测试。 单元测试是在bash脚本中初始化,执行和清理的。 该脚本通常包含init(),execute()和cleanup()函数。 但是它们不是强制性的。 我想测试它们是否定义。

之前,我是通过摸索和诱骗来源来做到这一点的,但这似乎是错误的。 有没有更优雅的方法可以做到这一点?

编辑:以下代码段就像一个超级按钮一样工作:

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

我认为您正在寻找"类型"命令。它会告诉您某些东西是函数,内置函数,外部命令还是只是未定义。例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ 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
2
3
4
5
$ g() { return; }
$ declare -f g > /dev/null; echo $?
0
$ declare -f j > /dev/null; echo $?
1


如果声明比测试快10倍,这似乎是显而易见的答案。

编辑:在下面,-f选项与BASH无关,请随时将其忽略。就个人而言,我很难记住哪个选项可以执行哪个操作,因此我只能同时使用两者。 -f显示功能,-F显示功能名称。

1
2
3
4
5
6
7
8
#!/bin/sh

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

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

声明的" -F"选项使它仅返回找到的函数的名称,而不返回整个内容。

使用/ dev / null不应对性能造成任何可衡量的影响,如果它让您担心的那么多:

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

或将两者结合起来,为自己带来无意义的享受。他们俩都工作。

1
2
3
4
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

借用其他解决方案和评论,我想到了这一点:

1
2
3
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' ]
}

用作...

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

它检查给定的参数是否是一个函数,并避免重定向和其他grepping。


疏通旧帖子...但是我最近使用了它,并测试了以下描述的两种替代方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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

这产生了:

1
2
3
4
5
6
7
8
real    0m0.064s
user    0m0.040s
sys     0m0.020s
type

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

声明是helluvalot更快!


测试不同的解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#!/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)

real 0m0,055s user 0m0,041s sys 0m0,004s exit code 0

test_declare2 (f is function)

real 0m0,042s user 0m0,022s sys 0m0,017s exit code 0

test_type (f is function)

real 0m2,200s user 0m1,619s sys 0m1,008s exit code 0

test_type2 (f is function)

real 0m0,746s user 0m0,534s sys 0m0,237s exit code 0

test_declare (f unset)

real 0m0,040s user 0m0,029s sys 0m0,010s exit code 1

test_declare2 (f unset)

real 0m0,038s user 0m0,038s sys 0m0,000s exit code 1

test_type (f unset)

real 0m2,438s user 0m1,678s sys 0m1,045s exit code 1

test_type2 (f unset)

real 0m0,805s user 0m0,541s sys 0m0,274s exit code 1

test_declare (f is string)

real 0m0,043s user 0m0,034s sys 0m0,007s exit code 1

test_declare2 (f is string)

real 0m0,039s user 0m0,035s sys 0m0,003s exit code 1

test_type (f is string)

real 0m2,394s user 0m1,679s sys 0m1,035s exit code 1

test_type2 (f is string)

real 0m0,851s user 0m0,554s sys 0m0,294s exit code 1

因此declare -F f似乎是最好的解决方案。


归结为使用"声明"来检查输出或退出代码。

输出样式:

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

用法:

1
isFunction some_name && echo yes || echo no

但是,如果使用内存,则重定向到null的速度比输出替换的速度要快(可以说,应该废除过时且过时的cmd方法,而应使用$(cmd)。)而且,如果找到find,则声明返回true / false。未找到,并且函数返回函数中最后一个命令的退出代码,因此通常不需要显式返回,并且由于检查错误代码比检查字符串值(甚至是空字符串)要快:

退出状态样式:

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

您可能会得到尽可能简洁和良性的评价。


从我对另一个答案的评论中(当我回到此页面时,我一直想念它)

1
2
3
4
5
6
7
8
$ 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

1
2
3
4
fn_exists()
{
   [[ $(type -t $1) == function ]] && return 0
}

更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
isFunc ()
{
    [[ $(type -t $1) == function ]]
}

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

我特别喜欢格雷戈里·约瑟夫的解决方案

但是我已经对其进行了一些修改,以克服"双引号丑陋的把戏":

1
2
3
4
5
6
7
8
9
10
function is_executable()
{
    typeset TYPE_RESULT="`type -t $1`"

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

这告诉您它是否存在,但不是它是一个函数

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

我将其改进为:

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

并像这样使用它:

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

可以使用'type'而不使用任何外部命令,但是您必须调用它两次,因此它的运行速度仍然是'declare'版本的两倍:

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

另外,这在POSIX sh中不起作用,因此,除了琐事之外,它毫无价值!


推荐阅读

    linux执行多次命令?

    linux执行多次命令?,系统,信息,标准,工作,情况,命令,周期性,服务,代码,时间,l

    linux并行化执行命令?

    linux并行化执行命令?,系统,工具,信息,命令,名称,网络,管理,首页,服务,暂停,L

    如何执行linux命令?

    如何执行linux命令?,单位,系统,网络,信息,权威,命令,文件,音乐,目录,选项,mv

    linux运行脚本命令?

    linux运行脚本命令?,系统,代码,服务,文件,工具,平台,网站,脚本,命令,方法,Lin

    linux打断执行的命令?

    linux打断执行的命令?,系统,状态,网站,标准,通用,客服,人员,名字,网络,暂停,L

    linux脚本命令修改?

    linux脚本命令修改?,密码,系统,文件,资料,工具,软件,基础,地址,标准,命令,lin

    linux命令没执行完?

    linux命令没执行完?,系统,设备,工具,情况,密码,状态,电脑,管理,材料,服务,Lin

    linux系统初始化命令?

    linux系统初始化命令?,系统,基础,工作,服务,信息,命令,软件,网络,密码,发行,l

    linux脚本命令教学?

    linux脚本命令教学?,标准,数据,系统,脚本,代码,流程,官网,底部,命令,变量,lin

    shell中执行linux命令?

    shell中执行linux命令?,系统,名称,环境,管理,工作,代码,技术,软件,经理,基础

    linux命令注释脚本?

    linux命令注释脚本?,代码,工具,名称,工作,脚本,发行,服务,环境,数据,基础,lin

    linux打印命令执行?

    linux打印命令执行?,信息,系统,工具,服务,命令,发行,基础,位置,设备,时间,怎

    脚本linux上运行命令?

    脚本linux上运行命令?,工具,代码,时间,密码,系统,环境,名字,位置,第三,下来,t

    linux执行两次命令?

    linux执行两次命令?,系统,信息,连续,名称,命令,初级,首页,工具,管理,终端,lin

    linux命令执行安装?

    linux命令执行安装?,软件,系统,管理,网站,官网,市场,中心,最新,灵活,工作,如

    linux执行一条新命令?

    linux执行一条新命令?,系统,工作,命令,管理,网络,服务,信息,目录,路径,脚本,L

    监控linux执行命令?

    监控linux执行命令?,系统,情况,数据,实时,网络,信息,状态,时间,设备,命令,如

    linux修改脚本的命令?

    linux修改脚本的命令?,系统,密码,服务,工作,工具,环境,信息,百度,代码,脚本,

    linux命令卡死不执行?

    linux命令卡死不执行?,系统,设备,数据,密码,工具,情况,软件,环境,分析,命令,l

    linux运行命令的脚本?

    linux运行命令的脚本?,系统,服务,工具,脚本,意外,技术,分析,文件,方法,命令,s