218

現在、bash から実行されるいくつかの単体テストを行っています。単体テストは、bash スクリプトで初期化、実行、およびクリーンアップされます。このスクリプトには通常、init()、execute()、および cleanup() 関数が含まれています。しかし、それらは必須ではありません。それらが定義されているかどうかをテストしたいと思います。

以前、ソースを grep して sed することでこれを行いましたが、間違っているように見えました。これを行うよりエレガントな方法はありますか?

編集:次のスニペットは魅力のように機能します:

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

14 に答える 14

229

このような:[[ $(type -t foo) == function ]] && echo "Foo exists"

組み込み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
于 2008-09-17T18:00:15.283 に答える
100

組み込みの bash コマンドには、定義されたすべての関数名を表示declareするオプションがあります。-Fname 引数を指定すると、それらの関数のどれが存在するかが表示され、すべて存在する場合はそれに応じてステータスが設定されます。

$ 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
于 2008-09-17T18:04:14.213 に答える
45

宣言がテストよりも 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

または、この 2 つを組み合わせて、無意味な楽しみ方をしてください。どちらも機能します。

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
于 2012-03-02T08:08:47.280 に答える
20

他のソリューションとコメントから借りて、私はこれを思いつきました:

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 を回避します。

于 2012-01-25T11:34:09.403 に答える
11

古い投稿を掘り起こしています...しかし、私は最近これを使用し、以下で説明されている両方の代替案をテストしました:

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

宣言は非常に高速です!

于 2010-02-12T15:57:48.127 に答える
7

さまざまなソリューションのテスト:

#!/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 は関数)

実際の 0m0,055s ユーザー 0m0,041s システム 0m0,004s 終了コード 0

test_declare2 (f は関数)

実際の 0m0,042s ユーザー 0m0,022s システム 0m0,017s 終了コード 0

test_type (f は関数)

実際の 0m2,200s ユーザー 0m1,619s システム 0m1,008s 終了コード 0

test_type2 (f は関数)

実際の 0m0,746s ユーザー 0m0,534s システム 0m0,237s 終了コード 0

test_declare (f unset)

実際の 0m0,040s ユーザー 0m0,029s システム 0m0,010s 終了コード 1

test_declare2 (f unset)

実際の 0m0,038s ユーザー 0m0,038s システム 0m0,000s 終了コード 1

test_type (f unset)

実際の 0m2,438s ユーザー 0m1,678s システム 0m1,045s 終了コード 1

test_type2 (f 未設定)

実際の 0m0,805s ユーザー 0m0,541s システム 0m0,274s 終了コード 1

test_declare (f は文字列)

実際の 0m0,043s ユーザー 0m0,034s システム 0m0,007s 終了コード 1

test_declare2 (f は文字列)

実際の 0m0,039s ユーザー 0m0,035s システム 0m0,003s 終了コード 1

test_type (f は文字列)

実際の 0m2,394s ユーザー 0m1,679s システム 0m1,035s 終了コード 1

test_type2 (f は文字列)

実際の 0m0,851s ユーザー 0m0,554s システム 0m0,294s 終了コード 1

したがってdeclare -F f、最適なソリューションのようです。

于 2016-11-19T13:03:01.177 に答える
7

要するに、'​​declare' を使用して出力をチェックするか、コードを終了します。

出力スタイル:

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

使用法:

isFunction some_name && echo yes || echo no

ただし、メモリが機能する場合は、null にリダイレクトする方が出力置換よりも高速です (そういえば、ひどい時代遅れの `cmd` メソッドを追放し、代わりに $(cmd) を使用する必要があります)。関数は関数内の最後のコマンドの終了コードを返すため、通常は明示的な戻り値は必要ありません。また、エラー コードのチェックは、文字列値 (null 文字列であっても) をチェックするよりも高速であるため、次のようになります。

終了ステータス スタイル:

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

それはおそらく、あなたが得ることができるのと同じくらい簡潔で無害です.

于 2012-05-28T18:09:26.923 に答える
3

これはそれが存在するかどうかを教えてくれますが、それが関数であるということではありません

fn_exists()
{
  type $1 >/dev/null 2>&1;
}
于 2009-10-08T22:46:38.930 に答える
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
$
于 2016-01-31T17:50:44.513 に答える
2

グレゴリー・ジョセフのソリューションが特に気に入りました

しかし、「二重引用符の醜いトリック」を克服するために少し変更しました。

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

    if [ "$TYPE_RESULT" == 'function' ]; then
        return 0
    else
        return 1
    fi
}
于 2012-02-27T14:29:31.820 に答える
2

私はそれを次のように改善します:

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
于 2009-10-08T22:18:08.660 に答える
0

外部コマンドなしで「type」を使用することは可能ですが、2 回呼び出す必要があるため、「declare」バージョンの約 2 倍遅くなります。

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

さらに、これは POSIX sh では機能しないため、トリビア以外ではまったく価値がありません。

于 2010-06-25T21:09:36.483 に答える