web-dev-qa-db-ja.com

関数がbashに存在するかどうかを判別

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

以前はソースをグレープおよびセディングしてこれを行いましたが、間違っているように見えました。これを行うよりエレガントな方法はありますか?

編集:次のスニペットは、チャームのように機能します。

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

「タイプ」コマンドを探していると思います。何かが関数、組み込み関数、外部コマンド、または単に定義されていないかどうかを示します。例:

$ 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
175
JBB
$ g() { return; }
$ declare -f g > /dev/null; echo $?
0
$ declare -f j > /dev/null; echo $?
1
69
Allan Wind

Declareがテストより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
36
Orwellophile

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

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

18
Grégory Joseph

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

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

declareはhelluvalotより高速です!

9

つまり、「declare」を使用して出力を確認するか、終了コードを返すことになります。

出力スタイル:

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

使用法:

isFunction some_name && echo yes || echo no

ただし、メモリが機能する場合、nullへのリダイレクトは出力の置換よりも高速です(つまり、ひどく古くなった `cmd`メソッドを破棄し、代わりに$(cmd)を使用する必要があります)。見つからない場合、関数は関数の最後のコマンドの終了コードを返すため、通常は明示的な戻り値は必要ありません。エラーコードのチェックは文字列値(ヌル文字列でも)をチェックするよりも速いためです。

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

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

それはおそらくあなたが得ることができるほど簡潔で良性です。

6
Scott

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

#!/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ユーザー0m0.024s sys 0m0.012s

宣言-F

実数0m0.030sユーザー0m0.020s sys 0m0.008s

grepで入力

実数0m1.772sユーザー0m0.084s sys 0m0.340s

varで入力

実際の0m0.770sユーザー0m0.096s sys 0m0.160s

declare -f(f unset)

実数0m0.031sユーザー0m0.028s sys 0m0.000s

宣言-F(f未設定)

実数0m0.031sユーザー0m0.020s sys 0m0.008s

grepで入力(f unset)

実際の0m1.859sユーザー0m0.100s sys 0m0.348s

varを使用して入力(f unset)

実数0m0.683sユーザー0m0.092s sys 0m0.160s

そう declare -F f && echo function f exists. || echo function f does not exist.が最善の解決策のようです。

5
jarno
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
$
3
Jonah

GrégoryJoseph のソリューションが特に気に入った

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

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

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

これは、存在するかどうかを示しますが、関数であることは示しません

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

別の回答に対する私のコメントから(このページに戻ったとき、私はそれを失います)

$ 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
2
qneill

私はそれを改善します:

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
1
user186791

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

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

さらに、これはPOSIX shでは機能しないため、雑学以外にはまったく価値がありません!

0
Noah Spurrier