निर्धारित करें कि क्या कोई फ़ंक्शन बैश में मौजूद है


187

वर्तमान में मैं कुछ यूनिट परीक्षण कर रहा हूं जो बैश से निष्पादित किए जाते हैं। इकाई परीक्षणों को बैश स्क्रिप्ट में आरंभ, निष्पादित और साफ किया जाता है। इस स्क्रिप्ट में सामान्य रूप से एक init (), निष्पादित () और क्लीनअप () फ़ंक्शन होते हैं। लेकिन वे अनिवार्य नहीं हैं। अगर वे परिभाषित हैं या नहीं तो मैं परीक्षण करना चाहूंगा।

मैंने ऐसा पहले स्रोत को तोड़-मरोड़ कर किया था, लेकिन यह गलत लग रहा था। क्या ऐसा करने का अधिक सुरुचिपूर्ण तरीका है?

संपादित करें: निम्नलिखित स्निपलेट एक आकर्षण की तरह काम करता है:

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

धन्यवाद। मैंने इसका उपयोग शेल लाइब्रेरी को लोड करते समय सशर्त रूप से कार्य के संस्करणों को परिभाषित करने के लिए किया था। fn_exists foo || foo() { :; }
हार्वे

2
आप का उपयोग करके grep बचा सकते हैं type -tऔर ==
रोलैंड वेबर

लोकेल नॉन-इंग्लिश होने पर काम नहीं करता है। फिनिश लोकेल का उपयोग करते समय और जर्मन का उपयोग करते समय type test_functionकहते हैं । test_function on funktio.ist eine Funktion
किममो लेह्टो

3
गैर-इंग्लिश स्थानों LC_ALL=Cके लिए रेसक्यू
gaRex

जवाबों:


191

मुझे लगता है कि आप 'टाइप' कमांड ढूंढ रहे हैं। यह आपको बताएगा कि क्या कोई फ़ंक्शन, अंतर्निहित फ़ंक्शन, बाहरी कमांड या बस परिभाषित नहीं है। उदाहरण:

$ 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

119
type -t $functionभोजन टिकट है।
एलन विंड

4
आपने इसे उत्तर के रूप में क्यों पोस्ट नहीं किया? :-)
टर्मिनस

क्योंकि मैंने घोषित उत्तर का उपयोग करते हुए अपना जवाब पोस्ट किया था :-)
एलन विंड

5
type [-t]आपको यह बताना अच्छा है कि एक चीज क्या है, लेकिन जब परीक्षण कुछ होता है, तो यह धीमी गति से होता है, क्योंकि आपको पाइप को पीछे खींचने या बैकटिक्स का उपयोग करना पड़ता है, जो दोनों एक उपप्रकार को स्पॉन करते हैं।
लल्केई

1
जब तक मैं गलत नहीं बोलता, तब तक टाइप करने के लिए एक मेल खाने वाली फ़ाइल की जाँच करने के लिए, एक न्यूनतम न्यूनतम पहुंच का प्रदर्शन करना होगा। @ ललोई, आप काफी सही हैं, लेकिन यह एक ऐसा विकल्प है जो न्यूनतम उत्पादन करता है, और आप अभी भी त्रुटी का उपयोग कर सकते हैं। आप एक उपप्रकार के बिना परिणाम प्राप्त कर सकते हैं, उदाहरण के लिए type -t realpath > /dev/shm/tmpfile ; read < /dev/shm/tmpfile(बुरा उदाहरण)। हालाँकि, डिक्लेयर सबसे अच्छा उत्तर है क्योंकि इसमें 0 डिस्क io है।
ऑरवेलोफाइल

79
$ g() { return; }
$ declare -f g > /dev/null; echo $?
0
$ declare -f j > /dev/null; echo $?
1

1
मेरे लिए कमाल का काम किया। विशेष रूप से मेरे शेल में टाइप के लिए -t फ्लैग नहीं है (मुझे "$ कमांड" टाइप करने में बहुत परेशानी हो रही थी)
डेनिस

2
वास्तव में, यह zsh (rc लिपियों के लिए उपयोगी) में भी काम करता है, और इस प्रकार के लिए grep की आवश्यकता नहीं होती है।
लल्केई

2
@ डेनिसहोडप की कोई आवश्यकता नहीं है type -t, आप इसके बजाय बाहर निकलने की स्थिति पर भरोसा कर सकते हैं। मैंने लंबे समय type program_name > /dev/null 2>&1 && program_name arguments || echo "error"से देखा है कि क्या मैं कुछ कह पाऊंगा या नहीं। जाहिर है type -tऔर उपरोक्त विधि भी प्रकार का पता लगाने की अनुमति देती है, न कि यह "कॉल करने योग्य"।
0xC0000022L 20

@ 0xC0000022L क्या होगा अगर program_name एक फ़ंक्शन नहीं है?
डेविड विनीकी

2
@ 0xC0000022L मैं बाहर निकलने की स्थिति का उपयोग करने के तरीके के बारे में बता रहा था कि क्या आपको पता नहीं है कि program_name एक फ़ंक्शन है, लेकिन अब मुझे लगता है कि जब आपने कहा था कि आपने स्पष्ट रूप से टाइप -t और उपरोक्त विधि भी प्रकार का पता लगाने की अनुमति देता है नहीं, बस यह "कॉल करने योग्य" है। माफ़ करना।
डेविड विनीकी

40

यदि डिक्लेयर टेस्ट की तुलना में 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

2
'-F' विकल्प बेमानी है।
राजिश

3
-Fविकल्प डेस (पोर्टेबिलिटी के लिए उपयोगी) zsh में नहीं मौजूद
Lloeki

-Fयह भी वास्तव में आवश्यक नहीं है: यह केवल फ़ंक्शन परिभाषा / शरीर को दबाने लगता है।
blueyed

1
@blueyed यह आवश्यक नहीं हो सकता है, लेकिन यह अत्यधिक वांछनीय है, हम एक फ़ंक्शन की पुष्टि करने की कोशिश कर रहे हैं, यह पूरी सामग्री नहीं है (जो कुछ अक्षम है) सूची। यदि कोई फ़ाइल का उपयोग करके मौजूद है, तो क्या आप जांच करेंगे cat "$fn" | wc -c? Zsh के लिए के रूप में, अगर बैश टैग आप में सुराग नहीं था, शायद सवाल ही होना चाहिए। "निर्धारित करें कि क्या कोई फ़ंक्शन बैश में मौजूद है"। मैं आगे यह बताना चाहूंगा कि जब -Fविकल्प zsh में मौजूद नहीं होता है, तो यह भी त्रुटि का कारण नहीं बनता है, इसलिए -f और -F दोनों का उपयोग चेक को zsh और bash दोनों में सफल होने की अनुमति देता है जो अन्यथा नहीं होगा ।
Orwellophile

@Orwellophile -Fका उपयोग zsh में फ्लोटिंग पॉइंट नंबरों के लिए किया जाता है। मैं यह नहीं देख सकता कि क्यों -Fयह बाश में बेहतर बनाता है ?! मुझे यह धारणा मिली कि यह declare -fबैश (रिटर्न कोड के बारे में) में समान काम करता है।
blueyed

18

अन्य समाधानों और टिप्पणियों से उधार लेकर, मैं इसके साथ आया:

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

यह जांचता है कि क्या दिया गया तर्क एक फ़ंक्शन है, और पुनर्निर्देशन और अन्य ग्रेपिंग से बचा जाता है।


अच्छा लगा, ग्रुप से मेरा फेवर! क्या आप तर्क के आसपास भी दोहरे उद्धरण नहीं चाहते हैं? जैसे[ $(type -t "$1")"" == 'function' ]
क्विकशिफ्टिन

धन्यवाद @quickshiftin; मुझे नहीं पता कि मैं उन दोहरे उद्धरणों को चाहता हूं, लेकिन आप शायद सही हैं, हालांकि .. क्या एक फ़ंक्शन को एक नाम के साथ भी घोषित किया जा सकता है जिसे उद्धृत करने की आवश्यकता होगी?
ग्रेजोरी जोसेफ

4
आप बैश का उपयोग कर रहे हैं, [[...]]इसके बजाय का उपयोग करें [...]और बोली हैक से छुटकारा पाएं। साथ ही बैकटिक्स फोर्क, जो धीमा है। declare -f $1 > /dev/nullइसके बजाय उपयोग करें ।
लल्केई

3
खाली तर्कों के साथ त्रुटियों से बचना, उद्धरणों को कम करना, और '=' पॉज़िक्स के अनुरूप समानता का उपयोग करना, इसे सुरक्षित रूप से कम किया जा सकता है :: fn_exists() { [ x$(type -t $1) = xfunction ]; }
qneill

10

एक पुरानी पोस्ट को डेज करना ... लेकिन मैंने हाल ही में इसका उपयोग किया था और इसके साथ वर्णित दोनों विकल्पों का परीक्षण किया था:

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

घोषणा तेजी से एक नर्क है!


1
इसे बिना grep के किया जा सकता है: test_type_nogrep () { a () { echo 'a' ;}; local b=$(type a); c=${b//is a function/}; [ $? = 0 ] && return 1 || return 0; }
qneill

@qneill मैंने अपने उत्तर में कुछ अधिक व्यापक परीक्षण किया ,
jarno

PIPE सबसे धीमा तत्व है। यह परीक्षण तुलना नहीं करता है typeऔर declare। इसके type | grepसाथ तुलना करता है declare। यह एक बड़ा अंतर है।
kyb

7

यह आउटपुट या एक्ज़िट कोड की जाँच करने के लिए 'डिक्लेयर' का उपयोग करने के लिए उबलता है।

आउटपुट शैली:

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

उपयोग:

isFunction some_name && echo yes || echo no

हालाँकि, यदि स्मृति कार्य करती है, तो आउटपुट रिड्यूसिटेशन की तुलना में नल को रीडायरेक्ट करना तेज है (बोलते हुए, भयानक और आउट-डेटेड `cmd` विधि को गायब कर दिया जाना चाहिए और इसके बजाय $ (cmd) का उपयोग किया जाना चाहिए।) और जब तक कि सही / गलत घोषित नहीं हो जाता है, तब तक रिटर्न की घोषणा की जाती है। नहीं मिला, और फ़ंक्शन फ़ंक्शन में अंतिम कमांड के एग्जिट कोड को वापस कर देते हैं, इसलिए एक स्पष्ट रिटर्न आमतौर पर आवश्यक नहीं होता है, और चूंकि त्रुटि कोड की जांच करना एक स्ट्रिंग मान (यहां तक ​​कि एक नल स्ट्रिंग) की जांच करने से तेज है:

बाहर निकलने की स्थिति शैली:

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

शायद आप के रूप में रसीला और सौम्य के बारे में के रूप में आप प्राप्त कर सकते हैं।


3
अधिकतम रसीला उपयोग के लिएisFunction() { declare -F "$1"; } >&-
नील

3
isFunction() { declare -F -- "$@" >/dev/null; }मेरी सिफारिश है यह नामों की एक सूची पर भी काम करता है (केवल सभी कार्य होने पर ही सफल होता है), नाम के साथ शुरू होने वाली समस्याओं के साथ कोई समस्या नहीं देता है -और, मेरी तरफ ( bash4.2.25), declareहमेशा विफल रहता है जब आउटपुट बंद होता है >&-, क्योंकि यह नाम नहीं लिख सकता है उस मामले में टालमटोल करने के लिए
Tino

और कृपया सचेत रहें, जो echoकभी-कभी कुछ प्लेटफार्मों पर "बाधित सिस्टम कॉल" के साथ विफल हो सकता है। उस स्थिति में "चेक && echo yes || echo no" अभी भी आउटपुट noहो सकता है अगर checkयह सच है।
टीनो

7

विभिन्न समाधानों का परीक्षण:

#!/bin/bash

test_declare () {
    declare -f f > /dev/null
}

test_declare2 () {
    declare -F f > /dev/null
}

test_type () {
    type -t f | grep -q 'function'
}

test_type2 () {
     [[ $(type -t f) = function ]]
}

funcs=(test_declare test_declare2 test_type test_type2)

test () {
    for i in $(seq 1 1000); do $1; done
}

f () {
echo 'This is a test function.'
echo 'This has more than one command.'
return 0
}
post='(f is function)'

for j in 1 2 3; do

    for func in ${funcs[@]}; do
        echo $func $post
        time test $func
        echo exit code $?; echo
    done

    case $j in
    1)  unset -f f
        post='(f unset)'
        ;;
    2)  f='string'
        post='(f is string)'
        ;;
    esac
done

आउटपुट उदाहरण:

test_declare (f फ़ंक्शन है)

वास्तविक 0m0,055s उपयोगकर्ता 0m0,041 sys 0m0,004s से बाहर निकलें कोड 0

test_declare2 (f फ़ंक्शन है)

वास्तविक 0m0,042 उपयोगकर्ता 0m0,022 sys 0m0,017s से बाहर निकलें कोड 0

test_type (f फ़ंक्शन है)

वास्तविक 0m2,200s उपयोगकर्ता 0m1,619 sys 0m1,008s से बाहर निकलें कोड 0

test_type2 (f फ़ंक्शन है)

वास्तविक 0m0,746 उपयोगकर्ता 0m0,534s sys 0m0,237s से बाहर निकलें कोड 0

test_declare (परेशान नहीं)

वास्तविक 0m0,040s उपयोगकर्ता 0m0,029s sys 0m0,010s निकास कोड 1

test_declare2 (f unset)

वास्तविक 0m0,038s उपयोगकर्ता 0m0,038s sys 0m0,000s से बाहर निकलें कोड 1

test_type (f unset)

वास्तविक 0m2,438s उपयोगकर्ता 0m1,678s sys 0m1,045s से बाहर निकलें कोड 1

test_type2 (f unset)

वास्तविक 0m0,805s उपयोगकर्ता 0m0,541 sys 0m0,274s से बाहर निकलें कोड 1

test_declare (f is string)

वास्तविक 0m0,043 उपयोगकर्ता 0m0,034 sys 0m0,007s निकास कोड 1

test_declare2 (f is string)

वास्तविक 0m0,039 उपयोगकर्ता 0m0,035s sys 0m0,003s से बाहर निकलें कोड 1

test_type (f is string)

वास्तविक 0m2,394s उपयोगकर्ता 0m1,679s sys 0m1,035s से बाहर निकलें कोड 1

test_type2 (f is string)

वास्तविक 0m0,851s उपयोगकर्ता 0m0,554s sys 0m0,294s निकास कोड 1

तो declare -F fसबसे अच्छा समाधान लगता है।


यहाँ ध्यान दें: declare -F fयदि शून्य पर मौजूद नहीं है तो गैर-शून्य मान नहीं लौटाता है, लेकिन हाँ पर रोक लगा सकता है। इसका उपयोग करने में सावधानी बरतें। declare -f f, दूसरे हाथ से, स्टडआउट पर फ़ंक्शन की परिभाषा को संलग्न करने की उम्मीद के रूप में काम करता है (जो कष्टप्रद हो सकता है ...)
मनोनेल विलेला

1
क्या आपने कोशिश की है test_type3 () { [[ $(type -t f) = function ]] ; }कि एक स्थानीय संस्करण को परिभाषित करने की सीमांत लागत है (हालांकि <10%)
ओलिवर

4

एक अन्य उत्तर पर मेरी टिप्पणी से (जो मुझे इस पृष्ठ पर वापस आने पर याद आ रही है)

$ 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

3
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
$

2

मैं इसमें सुधार करूंगा:

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

यह आपको बताता है कि क्या यह मौजूद है, लेकिन ऐसा नहीं है कि यह एक फ़ंक्शन है

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

2

मुझे विशेष रूप से ग्रेजरी जोसेफ का समाधान पसंद आया

लेकिन मैंने इसे "डबल कोट बदसूरत चाल" पर काबू पाने के लिए थोड़ा संशोधित किया है:

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

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

0

बिना किसी बाहरी आदेश के 'प्रकार' का उपयोग करना संभव है, लेकिन आपको इसे दो बार कॉल करना होगा, इसलिए यह अभी भी 'घोषित' संस्करण की तुलना में लगभग दो गुना धीमा है:

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

इसके अलावा यह POSIX श में काम नहीं करता है, इसलिए यह सामान्य ज्ञान को छोड़कर पूरी तरह से बेकार है!


test_type_nogrep () {a () {echo 'a';}; स्थानीय b = $ (प्रकार); c = $ {b // एक फ़ंक्शन /} है; [$? = 0] && वापसी 1 || वापसी 0; } - क्यूनील
एलेक्स रॉश
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.