确定bash中是否存在函数

确定bash中是否存在函数,bash,function,testing,scripting,Bash,Function,Testing,Scripting,目前我正在做一些从bash执行的单元测试。单元测试在bash脚本中初始化、执行和清理。此脚本通常包含init()、execute()和cleanup()函数。但它们不是强制性的。我想测试它们是否被定义 我以前是通过对源代码进行greping和seding来实现这一点的,但这似乎是错误的。有没有更优雅的方法 编辑:下面的剪贴画很有魅力: fn_exists() { LC_ALL=C type $1 | grep -q 'shell function' } 我想您正在寻找“type”命令。

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

我以前是通过对源代码进行greping和seding来实现这一点的,但这似乎是错误的。有没有更优雅的方法

编辑:下面的剪贴画很有魅力:

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

我想您正在寻找“type”命令。它会告诉您某个东西是函数、内置函数、外部命令还是未定义。例如:

$LC\u ALL=C类型foo
bash:type:foo:未找到
$LC_ALL=C类型ls
ls的别名为'ls--color=auto'
美元哪种类型
$LC_ALL=C类型
类型是一个shell内置项
$LC_ALL=C型-t rvm
功能
$if[-n“$(LC_ALL=C type-t rvm)”]&&&[“$(LC_ALL=C type-t rvm)”=函数];那么回波rvm就是一个函数;否则echo rvm不是一个函数;fi
rvm是一个函数

内置bash命令
declare
有一个选项
-F
,显示所有定义的函数名。如果给定名称参数,它将显示哪些函数存在,如果所有函数都存在,它将相应地设置状态:

$fn_exists(){declare-F“$1”>/dev/null;}
$unset f
$fn|u存在(&echo yes | | echo no
不
$f(){return;}
$fn|u存在(&echo yes | | echo no
对
我会将其改进为:

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

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

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

挖掘一个旧帖子。。。但我最近使用了此选项,并测试了以下两种选项:

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

申报要快得多

可以在不使用任何外部命令的情况下使用“type”,但您必须调用它两次,因此它的速度仍然是“declare”版本的两倍左右:

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

另外,这在POSIX sh中不起作用,所以它除了作为琐事之外毫无价值

借鉴其他解决方案和评论,我提出了以下建议:

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。

我特别喜欢来自

但我对它做了一点修改,以克服“双引号丑陋伎俩”:


如果declare比test快10倍,这似乎是显而易见的答案

编辑:下面的
-f
选项对于BASH来说是多余的,可以省略它。就我个人而言,我很难记住哪个选项能做哪个,所以我只能同时使用这两个选项-f显示函数,-f显示函数名

声明的“-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

它归结为使用“declare”检查输出或退出代码

输出样式:

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

isFunction some_name && echo yes || echo no
但是,如果内存可用,重定向到null比输出替换更快(说到这里,应该禁用糟糕且过时的'cmd'方法,而改用$(cmd)),而且由于declare在找到/未找到时返回true/false,函数返回函数中最后一个命令的退出代码,因此通常不需要显式返回,而且由于检查错误代码比检查字符串值(甚至是空字符串)要快:

退出状态样式:

isFunction() { declare -Ff "$1" >/dev/null; }
这大概是你能得到的最简洁、最温和的

fn_exists()
{
   [[ $(type -t $1) == function ]] && return 0
}
更新


测试不同的解决方案:

#!/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是函数)

实际0M0055S用户0M0041S系统0M0004S退出代码0

测试宣告2(f为功能)

实际0M0042S用户0M0022S系统0M0017S退出代码0

测试类型(f为功能)

实际0M2200S用户0M1619S系统0M1008S退出代码0

测试类型2(f为功能)

实际0M0746S用户0M0534S系统0M0237S退出代码0

测试声明(f未设置)

实际0M0040S用户0M0029S系统0M0010S退出代码1

测试公告2(未设置)

实际0M0038S用户0M0038S系统0M0000S退出代码1

测试类型(f未设置)

实际0M2438S用户0M1678S系统0M1045S退出代码1

测试类型2(f未设置)

实际0M0805S用户0M0541S系统0M0274S退出代码1

test_declare(f是字符串)

实际0M0043S用户0M0034S系统0M0007S退出代码1

测试_declare2(f是字符串)

实际0M0039S用户0M0035S系统0M0003S退出代码1

测试类型(f为字符串)

真正的0M2394S用户0M1679S系统0M1035S退出代码1

测试类型2(f为字符串)

实际0M0851S用户0M0554S系统0M0294S退出代码1


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

从我对另一个答案的评论中(当我回到本页时,我一直缺少这个答案)

函数的调用(如果已定义)。 已知函数名。假设名称为
my_function
,然后使用

[[ "$(type -t my_function)" == 'function' ]] && my_function;
# or
[[ "$(declare -fF my_function)" ]] && my_function;
变量中的函数名。如果我们声明
func=my_函数
,那么我们可以使用

[[ "$(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;
在函数内部使用
|
返回
的危险情况
以下组合将强制终止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; }
type-t$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;
# 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
    [[ "$(type -t my_function)" != 'function' ]] || { true; return; }