简体   繁体   English

判断bash中是否存在function

[英]Determine if a function exists in bash

Currently I'm doing some unit tests which are executed from bash. Unit tests are initialized, executed and cleaned up in a bash script.目前我正在做一些从 bash 执行的单元测试。单元测试在 bash 脚本中初始化、执行和清理。 This script usualy contains an init(), execute() and cleanup() functions.该脚本通常包含 init()、execute() 和 cleanup() 函数。 But they are not mandatory.但它们不是强制性的。 I'd like to test if they are or are not defined.我想测试它们是否已定义。

I did this previously by greping and seding the source, but it seemed wrong.我之前通过 greping 和 seding 源来做到这一点,但这似乎是错误的。 Is there a more elegant way to do this?有没有更优雅的方法来做到这一点?

Edit: The following sniplet works like a charm:编辑:以下代码片段很有魅力:

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

Like this: [[ $(type -t foo) == function ]] && echo "Foo exists"像这样: [[ $(type -t foo) == function ]] && echo "Foo exists"

The built-in type command will tell you whether something is a function, built-in function, external command, or just not defined.内置type命令将告诉您某物是函数、内置函数、外部命令还是未定义。

Additional examples:附加示例:

$ 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

The builtin bash command declare has an option -F that displays all defined function names.内置的 bash 命令declare有一个选项-F显示所有定义的函数名称。 If given name arguments, it will display which of those functions are exist, and if all do it will set status accordingly:如果给定 name 参数,它将显示哪些函数存在,如果全部存在,它将相应地设置状态:

$ fn_exists() { declare -F "$1" > /dev/null; }

$ unset f
$ fn_exists f && echo yes || echo no
no

$ f() { return; }
$ fn_exist f && echo yes || echo no
yes

If declare is 10x faster than test, this would seem the obvious answer.如果声明比测试快 10 倍,这似乎是显而易见的答案。

Edit: Below, the -f option is superfluous with BASH, feel free to leave it out.编辑:下面, -f选项对于 BASH 来说是多余的,请随意将其省略。 Personally, I have trouble remembering which option does which, so I just use both.就个人而言,我很难记住哪个选项执行哪个,所以我只使用两者。 -f shows functions, and -F shows function names. -f显示函数, -F显示函数名称。

#!/bin/sh

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

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

The "-F" option to declare causes it to only return the name of the found function, rather than the entire contents.声明的“-F”选项使其仅返回找到的函数的名称,而不是整个内容。

There shouldn't be any measurable performance penalty for using /dev/null, and if it worries you that much:使用 /dev/null 不应该有任何可衡量的性能损失,如果它让您如此担心:

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

Or combine the two, for your own pointless enjoyment.或者将两者结合起来,为您自己的无意义享受。 They both work.他们都工作。

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

Borrowing from other solutions and comments, I came up with this:借鉴其他解决方案和评论,我想出了这个:

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

Used as ...用作...

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

It checks if the given argument is a function, and avoids redirections and other grepping.它检查给定的参数是否是一个函数,并避免重定向和其他 grepping。

Dredging up an old post ... but I recently had use of this and tested both alternatives described with :挖出一个旧帖子......但我最近使用了这个并测试了用以下描述的两种替代方案:

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

this generated :这产生了:

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

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

declare is a helluvalot faster !声明速度更快!

It boils down to using 'declare' to either check the output or exit code.归结为使用“声明”来检查输出或退出代码。

Output style:输出样式:

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

Usage:用法:

isFunction some_name && echo yes || echo no

However, if memory serves, redirecting to null is faster than output substitution (speaking of, the awful and out-dated `cmd` method should be banished and $(cmd) used instead.) And since declare returns true/false if found/not found, and functions return the exit code of the last command in the function so an explicit return is usually not necessary, and since checking the error code is faster than checking a string value (even a null string):但是,如果内存可用,重定向到 null 比输出替换更快(说到,可怕的和过时的 `cmd` 方法应该被放逐,而使用 $(cmd) 代替。)并且因为如果找到/,声明返回真/假/未找到,并且函数返回函数中最后一个命令的退出代码,因此通常不需要显式返回,并且因为检查错误代码比检查字符串值(甚至是空字符串)更快:

Exit status style:退出状态样式:

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

That's probably about as succinct and benign as you can get.这可能是你所能得到的尽可能简洁和良性的。

Testing different solutions:测试不同的解决方案:

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

outputs eg:输出例如:

test_declare (f is function) test_declare (f 是函数)

real 0m0,055s user 0m0,041s sys 0m0,004s exit code 0真实 0m0,055s 用户 0m0,041s sys 0m0,004s 退出代码 0

test_declare2 (f is function) test_declare2(f 是函数)

real 0m0,042s user 0m0,022s sys 0m0,017s exit code 0真实 0m0,042s 用户 0m0,022s sys 0m0,017s 退出代码 0

test_type (f is function) test_type (f 是函数)

real 0m2,200s user 0m1,619s sys 0m1,008s exit code 0真正的 0m2,200s 用户 0m1,619s sys 0m1,008s 退出代码 0

test_type2 (f is function) test_type2(f 是函数)

real 0m0,746s user 0m0,534s sys 0m0,237s exit code 0真实 0m0,746s 用户 0m0,534s sys 0m0,237s 退出代码 0

test_declare (f unset) test_declare (f 未设置)

real 0m0,040s user 0m0,029s sys 0m0,010s exit code 1真正的 0m0,040s 用户 0m0,029s sys 0m0,010s 退出代码 1

test_declare2 (f unset) test_declare2 (f 未设置)

real 0m0,038s user 0m0,038s sys 0m0,000s exit code 1真实 0m0,038s 用户 0m0,038s sys 0m0,000s 退出代码 1

test_type (f unset) test_type (f 未设置)

real 0m2,438s user 0m1,678s sys 0m1,045s exit code 1真实 0m2,438s 用户 0m1,678s sys 0m1,045s 退出代码 1

test_type2 (f unset) test_type2 (f 未设置)

real 0m0,805s user 0m0,541s sys 0m0,274s exit code 1真实 0m0,805s 用户 0m0,541s sys 0m0,274s 退出代码 1

test_declare (f is string) test_declare (f 是字符串)

real 0m0,043s user 0m0,034s sys 0m0,007s exit code 1真实 0m0,043s 用户 0m0,034s sys 0m0,007s 退出代码 1

test_declare2 (f is string) test_declare2(f 是字符串)

real 0m0,039s user 0m0,035s sys 0m0,003s exit code 1真实 0m0,039s 用户 0m0,035s sys 0m0,003s 退出代码 1

test_type (f is string) test_type (f 是字符串)

real 0m2,394s user 0m1,679s sys 0m1,035s exit code 1真实 0m2,394s 用户 0m1,679s sys 0m1,035s 退出代码 1

test_type2 (f is string) test_type2 (f 是字符串)

real 0m0,851s user 0m0,554s sys 0m0,294s exit code 1真实 0m0,851s 用户 0m0,554s sys 0m0,294s 退出代码 1

So declare -F f seems to be the best solution.所以declare -F f似乎是最好的解决方案。

From my comment on another answer (which I keep missing when I come back to this page)从我对另一个答案的评论(当我回到这个页面时我一直想念)

$ 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

This tells you if it exists, but not that it's a function这告诉你它是否存在,但不是它是一个函数

fn_exists()
{
  type $1 >/dev/null 2>&1;
}
fn_exists()
{
   [[ $(type -t $1) == function ]] && return 0
}

update更新

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

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

I would improve it to:我会将其改进为:

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

And use it like this:并像这样使用它:

fn_exists test_function
if [ $? -eq 0 ]; then
    echo 'Function exists!'
else
    echo 'Function does not exist...'
fi

I particularly liked solution from Grégory Joseph我特别喜欢Grégory Joseph 的解决方案

But I've modified it a little bit to overcome "double quote ugly trick":但是我对其进行了一些修改以克服“双引号丑陋的技巧”:

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

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

It is possible to use 'type' without any external commands, but you have to call it twice, so it still ends up about twice as slow as the 'declare' version:可以在没有任何外部命令的情况下使用 'type',但是你必须调用它两次,所以它最终仍然比 'declare' 版本慢两倍:

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

Plus this doesn't work in POSIX sh, so it's totally worthless except as trivia!此外,这在 POSIX sh 中不起作用,因此除了作为琐事之外,它完全没有价值!

Invocation of a function if defined.如果已定义,则调用函数。

Known function name .已知函数名 Let's say the name is my_function , then use假设名称是my_function ,然后使用

[[ "$(type -t my_function)" == 'function' ]] && my_function;
# or
[[ "$(declare -fF my_function)" ]] && my_function;

Function name in a variable .变量中的函数名 If we declare func=my_function , then we can use如果我们声明func=my_function ,那么我们可以使用

[[ "$(type -t $func)" == 'function' ]] && $func;
# or
[[ "$(declare -fF $func)" ]] && $func;

A logic inversion with the same result .具有相同结果的逻辑反转 All at once.一次全部。 We use ||我们使用|| instead of && here.而不是这里的&&

[[ "$(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;

Danger situation for using ||使用||危险情况with return inside a function在函数内return
The following combination will force you shell process to be terminated.以下组合将强制您终止 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

This is because the above is an equivalent of这是因为上面的等价于

set -e
function run_if_exists(){
    return 1;
}
run_if_exists

which will always fail.这将永远失败。
To prevent this, you have to use || { true; return; }为了防止这种情况,您必须使用|| { true; return; } || { true; return; } || { true; return; } or something else in such the precondition of your function. || { true; return; }或别的东西在你的功能这样的前提。

    [[ "$(type -t my_function)" != 'function' ]] || { true; return; }

You can check them in 4 ways您可以通过 4 种方式检查它们

fn_exists() { type -t $1 >/dev/null && echo 'exists'; }
fn_exists() { declare -F $1 >/dev/null && echo 'exists'; }
fn_exists() { typeset -F $1 >/dev/null && echo 'exists'; }
fn_exists() { compgen -A function $1 >/dev/null && echo 'exists'; }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM