سؤال

أقوم حاليًا بإجراء بعض اختبارات الوحدة التي يتم تنفيذها من 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

إذا كان الإعلان أسرع بـ 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

يتحقق مما إذا كانت الوسيطة المحددة دالة، ويتجنب عمليات إعادة التوجيه وغيرها من عمليات الإمساك.

ازالة مشاركة قديمة...لكنني استخدمت هذا مؤخرًا واختبرت كلا البديلين الموصوفين بـ:

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

أعلن هو helluvalot أسرع!

يتلخص الأمر في استخدام "الإعلان" إما للتحقق من رمز الإخراج أو الخروج.

نمط الإخراج:

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

user 0m0.037s user 0m0.024s sys 0m0.012s

أعلن -F

المستخدم الحقيقي 0M0.030S 0M0.020S SYS 0M0.008S

اكتب مع grep

user 0m1.772s user 0m0.084s sys 0m0.340s

اكتب مع فار

المستخدم الحقيقي 0M0.770S 0M0.096S SYS 0M0.160S

أعلن -f (f غير محدد)

ureal 0m0.031s user 0m0.028s sys 0m0.000s

أعلن -F (f غير محدد)

user 0m0.031s user 0m0.020s sys 0m0.008s

اكتب باستخدام grep (f غير محدد)

user 0m1.859s user 0m0.100s sys 0m0.348s

اكتب مع var (f unset)

user 0m0.683s user 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
$

هذا يخبرك إذا كانت موجودة، ولكن ليس أنها وظيفة

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