it-swarm-eu.dev

Bestimmen Sie, ob eine Funktion in bash vorhanden ist

Derzeit mache ich einige Unit-Tests, die von Bash ausgeführt werden. Unit-Tests werden in einem Bash-Skript initialisiert, ausgeführt und bereinigt. Dieses Skript enthält normalerweise die Funktionen init (), execute () und cleanup (). Sie sind aber nicht verpflichtend. Ich möchte testen, ob sie definiert sind oder nicht.

Ich habe dies zuvor getan, indem ich die Quelle gegriffen und ausgesät habe, aber es schien falsch. Gibt es eine elegantere Möglichkeit, dies zu tun?

Edit: Das folgende Sniplet funktioniert wie ein Zauber:

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

Ich denke, Sie suchen nach dem Befehl "Typ". Hier erfahren Sie, ob es sich um eine Funktion, eine integrierte Funktion, einen externen Befehl oder nur um eine nicht definierte Funktion handelt. Beispiel:

$ 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

Wenn declare 10x schneller ist als test, scheint dies die naheliegende Antwort zu sein.

Edit: Unten das -f Option ist mit BASH überflüssig, lassen Sie es einfach weg. Persönlich fällt es mir schwer, mich daran zu erinnern, welche Option welche Funktion hat. Daher verwende ich einfach beide. - f zeigt Funktionen und - F zeigt Funktionsnamen.

#!/bin/sh

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

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

Die zu deklarierende Option "-F" bewirkt, dass nur der Name der gefundenen Funktion und nicht der gesamte Inhalt zurückgegeben wird.

Es sollte keine messbaren Leistungseinbußen für die Verwendung von/dev/null geben, und wenn es Sie so sehr beunruhigt:

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

Oder kombinieren Sie beides für Ihren eigenen sinnlosen Genuss. Sie arbeiten beide.

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

In Anlehnung an andere Lösungen und Kommentare habe ich Folgendes gefunden:

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

Benutzt als ...

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

Es prüft, ob das angegebene Argument eine Funktion ist, und vermeidet Umleitungen und anderes Greifen.

18
Grégory Joseph

Einen alten Posten ausgraben ... aber ich hatte kürzlich Gebrauch davon und testete beide beschriebenen Alternativen mit:

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

dies erzeugte:

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

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

deklarieren ist ein helluvalot schneller!

9

Es läuft darauf hinaus, 'declare' zu verwenden, um entweder den Ausgabe- oder den Beendigungscode zu überprüfen.

Ausgabestil:

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

Verwendung:

isFunction some_name && echo yes || echo no

Wenn der Speicher belegt ist, ist das Umleiten auf null schneller als das Ersetzen der Ausgabe. nicht gefunden, und Funktionen geben den Exit-Code des letzten Befehls in der Funktion zurück, sodass eine explizite Rückgabe normalerweise nicht erforderlich ist. Da die Überprüfung des Fehlercodes schneller ist als die Überprüfung eines Zeichenfolgenwerts (sogar einer Nullzeichenfolge):

Statusstil beenden:

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

Das ist wahrscheinlich so prägnant und gutartig wie möglich.

6
Scott

Testgeschwindigkeit verschiedener Lösungen

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

ausgänge z.

deklariere -f

echte 0m0.037s Benutzer 0m0.024s sys 0m0.012s

deklariere -F

echte 0m0.030s Benutzer 0m0.020s sys 0m0.008s

tippe mit grep

echte 0m1.772s Benutzer 0m0.084s sys 0m0.340s

typ mit var

echte 0m0.770s Benutzer 0m0.096s sys 0m0.160s

deklariere -f (f nicht gesetzt)

echte 0m0.031s Benutzer 0m0.028s sys 0m0.000s

deklariere -F (f unset)

echte 0m0.031s Benutzer 0m0.020s sys 0m0.008s

tippe mit grep (f unset)

echte 0m1.859s Benutzer 0m0.100s sys 0m0.348s

tippe mit var (f unset)

echte 0m0.683s Benutzer 0m0.092s sys 0m0.160s

So declare -F f && echo function f exists. || echo function f does not exist. scheint die beste Lösung zu sein.

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

pdate

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

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

Besonders gefallen hat mir die Lösung von Grégory Joseph

Aber ich habe es ein wenig modifiziert, um den "hässlichen Trick mit doppelten Anführungszeichen" zu überwinden:

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

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

Dies sagt Ihnen, ob es existiert, aber nicht, dass es eine Funktion ist

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

Aus meinem Kommentar zu einer anderen Antwort (die mir immer wieder fehlt, wenn ich auf diese Seite zurückkomme)

$ 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

Ich würde es verbessern, um:

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

Und benutze es so:

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

Es ist möglich, 'type' ohne externe Befehle zu verwenden, aber Sie müssen es zweimal aufrufen, sodass es immer noch ungefähr doppelt so langsam ist wie die 'declare'-Version:

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

Außerdem funktioniert dies in POSIX sh nicht, so dass es bis auf Kleinigkeiten völlig wertlos ist!

0
Noah Spurrier