現在、bashから実行されるいくつかの単体テストを行っています。単体テストは、bashスクリプトで初期化、実行、およびクリーンアップされます。このスクリプトには通常、init()、execute()、およびcleanup()関数が含まれています。しかし、それらは必須ではありません。定義されているかどうかをテストしたいと思います。
以前はソースをグレープおよびセディングしてこれを行いましたが、間違っているように見えました。これを行うよりエレガントな方法はありますか?
編集:次のスニペットは、チャームのように機能します。
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
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
他のソリューションとコメントから借りて、私はこれを思いつきました:
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を回避します。
古い投稿を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より高速です!
つまり、「declare」を使用して出力を確認するか、終了コードを返すことになります。
出力スタイル:
isFunction() { [[ "$(declare -Ff "$1")" ]]; }
使用法:
isFunction some_name && echo yes || echo no
ただし、メモリが機能する場合、nullへのリダイレクトは出力の置換よりも高速です(つまり、ひどく古くなった `cmd`メソッドを破棄し、代わりに$(cmd)を使用する必要があります)。見つからない場合、関数は関数の最後のコマンドの終了コードを返すため、通常は明示的な戻り値は必要ありません。エラーコードのチェックは文字列値(ヌル文字列でも)をチェックするよりも速いためです。
終了ステータススタイル:
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ユーザー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.
が最善の解決策のようです。
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
$
GrégoryJoseph のソリューションが特に気に入った
しかし、「二重引用符のugいトリック」を克服するために少し変更しました。
function is_executable()
{
typeset TYPE_RESULT="`type -t $1`"
if [ "$TYPE_RESULT" == 'function' ]; then
return 0
else
return 1
fi
}
これは、存在するかどうかを示しますが、関数であることは示しません
fn_exists()
{
type $1 >/dev/null 2>&1;
}
別の回答に対する私のコメントから(このページに戻ったとき、私はそれを失います)
$ 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
外部コマンドなしで「タイプ」を使用することは可能ですが、2回呼び出す必要があるため、「宣言」バージョンの約2倍遅くなります。
test_function () {
! type -f $1 >/dev/null 2>&1 && type -t $1 >/dev/null 2>&1
}
さらに、これはPOSIX shでは機能しないため、雑学以外にはまったく価値がありません!