it-swarm-id.com

Tentukan apakah ada fungsi di bash

Saat ini saya sedang melakukan beberapa unit test yang dijalankan dari bash. Tes unit diinisialisasi, dijalankan, dan dibersihkan dalam skrip bash. Script ini biasanya berisi fungsi init (), execute () dan cleanup (). Tetapi mereka tidak wajib. Saya ingin menguji apakah mereka didefinisikan atau tidak.

Saya melakukan ini sebelumnya dengan menangkap dan membius sumbernya, tetapi tampaknya salah. Apakah ada cara yang lebih elegan untuk melakukan ini?

Sunting: Sniplet berikut ini berfungsi seperti pesona:

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

Saya pikir Anda sedang mencari perintah 'ketik'. Ini akan memberi tahu Anda apakah ada fungsi, fungsi bawaan, perintah eksternal, atau tidak didefinisikan. Contoh:

$ 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

Jika menyatakan 10x lebih cepat dari tes, ini akan menjadi jawaban yang jelas.

Sunting: Di bawah ini, -f opsi tidak perlu dengan BASH, jangan ragu untuk meninggalkannya. Secara pribadi, saya kesulitan mengingat opsi mana yang melakukan itu, jadi saya hanya menggunakan keduanya. - f menunjukkan fungsi, dan - F menunjukkan nama fungsi.

#!/bin/sh

function_exists() {
    declare -f -F $1 > /dev/null
    return $?
}

function_exists function_name && echo Exists || echo No such function

Opsi "-F" untuk menyatakan menyebabkannya hanya mengembalikan nama fungsi yang ditemukan, daripada seluruh konten.

Seharusnya tidak ada penalti kinerja yang terukur untuk penggunaan/dev/null, dan jika itu membuat Anda sangat khawatir:

fname=`declare -f -F $1`
[ -n "$fname" ]    && echo Declare -f says $fname exists || echo Declare -f says $1 does not exist

Atau kombinasikan keduanya, untuk kesenangan tak berguna Anda sendiri. Keduanya bekerja.

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

Meminjam dari solusi dan komentar lain, saya datang dengan ini:

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' ]
}

Digunakan sebagai ...

if ! fn_exists $FN; then
    echo "Hey, $FN does not exist ! Duh."
    exit 2
fi

Ia memeriksa apakah argumen yang diberikan adalah fungsi, dan menghindari pengalihan dan grepping lainnya.

18
Grégory Joseph

Pengerukan pos lama ... tapi saya baru-baru ini menggunakan ini dan menguji kedua alternatif yang dijelaskan dengan:

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

ini dihasilkan:

real    0m0.064s
user    0m0.040s
sys     0m0.020s
type

real    0m2.769s
user    0m1.620s
sys     0m1.130s

menyatakan adalah helluvalot lebih cepat!

9
jonathanserafini

Itu bermuara menggunakan 'menyatakan' baik untuk memeriksa output atau kode keluar.

Gaya keluaran:

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

Pemakaian:

isFunction some_name && echo yes || echo no

Namun, jika memori berfungsi, pengalihan ke null lebih cepat daripada substitusi output (berbicara tentang, metode `cmd` yang mengerikan dan ketinggalan zaman harus dibuang dan $ (cmd) digunakan sebagai gantinya.) Dan karena menyatakan pengembalian benar/salah jika ditemukan/tidak ditemukan, dan fungsi mengembalikan kode keluar dari perintah terakhir dalam fungsi sehingga pengembalian eksplisit biasanya tidak diperlukan, dan karena memeriksa kode kesalahan lebih cepat daripada memeriksa nilai string (bahkan string nol):

Keluar dari gaya status:

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

Itu mungkin tentang singkat dan jinak yang Anda bisa dapatkan.

6
Scott

Menguji kecepatan berbagai solusi

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

output misalnya:

mendeklarasikan -f

0m0.037s nyata pengguna 0m0.024s sys 0m0.012s

mendeklarasikan -F

0m0.030s nyata pengguna 0m0.020s sys 0m0.008s

ketik dengan grep

0m1.772s nyata pengguna 0m0.084s sys 0m0.340s

ketik dengan var

0m0.770s nyata pengguna 0m0.096s sys 0m0.160s

mendeklarasikan -f (f unset)

0m0.031s nyata pengguna 0m0.028s sys 0m0.000s

mendeklarasikan -F (f unset)

0m0.031s nyata pengguna 0m0.020s sys 0m0.008s

ketik dengan grep (f unset)

pengguna 0m1.859s nyata 0m0.100s sys 0m0.348s

ketik dengan var (f unset)

nyata 0m0.683 pengguna 0m0.092s sys 0m0.160s

Jadi declare -F f && echo function f exists. || echo function f does not exist. tampaknya menjadi solusi terbaik.

5
jarno
fn_exists()
{
   [[ $(type -t $1) == function ]] && return 0
}

pembaruan

isFunc () 
{ 
    [[ $(type -t $1) == function ]]
}

$ isFunc isFunc
$ echo $?
0
$ isFunc dfgjhgljhk
$ echo $?
1
$ isFunc psgrep && echo yay
yay
$
3
Jonah

Saya sangat menyukai solusi dari Grégory Joseph

Tetapi saya telah memodifikasinya sedikit untuk mengatasi "trik jelek kutipan ganda":

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

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

Ini memberi tahu Anda jika itu ada, tetapi bukan itu fungsinya

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

Dari komentar saya pada jawaban lain (yang selalu saya lewatkan ketika saya kembali ke halaman ini)

$ 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

Saya akan meningkatkannya menjadi:

fn_exists()
{
    type $1 2>/dev/null | grep -q 'is a function'
}

Dan gunakan seperti ini:

fn_exists test_function
if [ $? -eq 0 ]; then
    echo 'Function exists!'
else
    echo 'Function does not exist...'
fi
1
user186791

Dimungkinkan untuk menggunakan 'ketik' tanpa perintah eksternal, tetapi Anda harus memanggilnya dua kali, sehingga masih berakhir sekitar dua kali lebih lambat dari versi 'nyatakan':

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

Plus ini tidak berfungsi di POSIX sh, jadi ini sama sekali tidak berharga kecuali sebagai hal sepele!

0
Noah Spurrier