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

我之前通过 grep 和 sed 源来做到这一点,但这似乎是错误的。有没有更优雅的方法来做到这一点?

编辑:下面的代码片段就像一个魅力:

fn_exists()
{
    LC_ALL=C type $1 | grep -q 'shell function'
}
有帮助吗?

解决方案

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

$ 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

其他提示

$ g() { return; }
$ declare -f g > /dev/null; echo $?
0
$ declare -f j > /dev/null; echo $?
1

如果声明比测试快 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

借鉴其他解决方案和评论,我想出了这个:

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

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

挖一个旧帖子...但我最近使用了这个并测试了以下描述的两种替代方案:

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

声明速度快得多!

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

输出样式:

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

用法:

isFunction some_name && echo yes || echo no

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

退出状态样式:

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

这可能是您所能得到的最简洁、最友善的说法了。

不同方案的测试速度

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

输出例如:

声明-f

实际 0m0.037s 用户 0 分 0.024 秒 系统0分0.012秒

声明-F

真实 0m0.030s 用户 0 分 0.020 秒 系统0分0.008秒

使用 grep 键入

真正的 0m1.772s 用户 0m0.084s 系统0分0.340秒

使用 var 类型

真实 0m0.770s 用户 0 分 0.096 秒 系统0分0.160秒

声明 -f(f 未设置)

真实 0m0.031s 用户 0 分 0.028 秒 系统0分0.000秒

声明 -F(f 未设置)

真实 0m0.031s 用户 0 分 0.020 秒 系统0分0.008秒

使用 grep 键入(f 未设置)

真实 0m1.859s 用户 0m0.100s 系统0分0.348秒

使用 var 类型(f 未设置)

真实 0m0.683s 用户 0分0.092秒 系统0分0.160秒

所以 declare -F f && echo function f exists. || echo function f does not exist. 似乎是最好的解决方案。

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
$

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

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

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

但我对其进行了一些修改以克服“双引号丑陋技巧”:

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

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

来自我对另一个答案的评论(当我回到此页面时我一直错过这个答案)

$ 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

我会将其改进为:

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

可以在没有任何外部命令的情况下使用“type”,但是您必须调用它两次,因此它最终仍然比“declare”版本慢大约两倍:

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

另外,这在 POSIX sh 中不起作用,所以除了作为琐事之外,它完全没有价值!

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top