फं

फंक्शन में Bash

{one: "1 अभ्यास", other: "%{count} अभ्यास"}

फंक्शन के बारे में

बहुत-सी Bash स्क्रिप्ट सीधे आदेश देने वाली शैली में लिखी जाती हैं: पहले एक कमांड चलाइए, फिर दूसरी, और इसी तरह। कभी-कभी आपको कई कमांडों को एक साथ इस तरह रखना पड़ता है कि वे मिलकर एक ही उद्देश्य पूरा करें। यहीं फंक्शन काम आते हैं।

फंक्शन बनाना

फंक्शन बनाने का तरीका यह है:

my_function () {
    COMMANDS
}

गोल ब्रैकेट का खाली जोड़ा बस यह बताता है कि आप एक फंक्शन बना रहे हैं। इनके अंदर कुछ नहीं लिखा जाता।

फंक्शन के पैरामीटर

एक बार बन जाने के बाद फंक्शन किसी भी दूसरी कमांड की तरह काम करते हैं (चाहे वह बिल्ट-इन हो या न हो)। किसी भी कमांड की तरह आप अपने फंक्शन को आर्गुमेंट दे सकते हैं। फंक्शन के अंदर आप इन आर्गुमेंटों तक पोज़िशनल पैरामीटर, $1, $2 आदि के ज़रिए पहुँचते हैं। (याद कीजिए, हमने पोज़िशनल पैरामीटर के बारे में वेरिएबल कॉन्सेप्ट में पढ़ा था।)

Advanced

फंक्शन के अंदर विशेष पैरामीटर $0 नहीं बदलता; वह अब भी चल रही स्क्रिप्ट का नाम ही रहता है। जो फंक्शन इस समय चल रहा है, वह अपना नाम $FUNCNAME वेरिएबल से पा सकता है।

मैनुअल में 3.4.2 विशेष पैरामीटर देखिए।

वेरिएबल

आप फंक्शन के अंदर वेरिएबल बना सकते हैं। अगर आप वेरिएबलों को local कमांड से घोषित करते हैं, तो उस वेरिएबल का स्कोप सिर्फ मौजूदा फंक्शन तक सीमित रहता है (और उन फंक्शनों तक भी जिन्हें वह कॉल करता है)। वरना वेरिएबल ग्लोबल स्कोप में रखा जाता है।

लोकल वेरिएबल का नाम किसी ग्लोबल वेरिएबल जैसा ही हो सकता है। ऐसे में लोकल वेरिएबल उस ग्लोबल वेरिएबल को "छिपा" देता है। जैसे, फंक्शन के अंदर घोषित कोई लोकल वेरिएबल उसी नाम के ग्लोबल वेरिएबल को छिपा देता है: उस नाम के सारे संदर्भ और असाइनमेंट लोकल वेरिएबल पर लागू होते हैं, और ग्लोबल वेरिएबल ज्यों का त्यों रहता है। फंक्शन का काम पूरा होते ही ग्लोबल वेरिएबल फिर से दिखने लगता है।

x=5

myfunc () {
    local x=100
    echo "in my function, $x == 100"
}

echo "in the global scope, $x == 5"

myfunc

echo "back in the global scope, $x == 5"

इसका आउटपुट होता है

in the global scope, 5 == 5
in my function, 100 == 100
back in the global scope, 5 == 5

फंक्शन के अंदर आप कॉलर के स्कोप के वेरिएबलों तक पहुँच सकते हैं। यानी आप ग्लोबल वेरिएबल इस्तेमाल कर सकते हैं, और वे लोकल वेरिएबल भी जो कॉलर में (या किसी ऐसे फंक्शन में जो कॉलर को कॉल करता है) घोषित हुए थे।

Advanced

तकनीकी रूप से देखें तो "ग्लोबल" सही शब्द नहीं है। फंक्शन में किसी वेरिएबल की वैल्यू निकालने के लिए Bash कॉल स्टैक में ऊपर की ओर, ग्लोबल स्कोप तक जाता है, और ऐसा फंक्शन ढूँढता है जहाँ उस वेरिएबल का नाम घोषित हुआ हो।

यह उदाहरण मैनुअल के शेल फंक्शन हिस्से से लिया गया है:

func1() {
    local var='func1 local'
    func2
}

func2() {
    echo "In func2, var = $var"
}

var=global
func1
func2

आउटपुट यह है:

In func2, var = func1 local
In func2, var = global

इसी तरह, किसी वेरिएबल को वैल्यू असाइन करने का मतलब है उसे उसी स्कोप में असाइन करना जहाँ वह घोषित हुआ था। यह "दूर से की गई कार्रवाई" ऐसा कोड बना सकती है जिसे समझना मुश्किल हो, क्योंकि यह हमेशा साफ नहीं होता कि किसी वेरिएबल को वैल्यू कहाँ असाइन हुई थी।

Advanced

कॉल स्टैक को FUNCNAME ऐरे वेरिएबल की मदद से देखा जा सकता है।

रिटर्न वैल्यू

किसी भी कमांड की तरह फंक्शन का भी एक एग्ज़िट स्टेटस होता है। डिफॉल्ट रूप से फंक्शन का स्टेटस सबसे अंत में चली गई कमांड का एग्ज़िट स्टेटस होता है।

फंक्शन से किसी तय एग्ज़िट स्टेटस के साथ लौटने के लिए आप return कमांड इस्तेमाल कर सकते हैं।

check_password () {
    if [[ $1 == "secret" ]]; then
        return 0
    else
        return 1
    fi
}

read -sp "Enter your password: " pass

if check_password "$pass"; then
    echo "Correct!"
else
    echo "Wrong password."
fi

return को बिना किसी आर्गुमेंट के इस्तेमाल करने पर वह सबसे अंत में चली गई कमांड का स्टेटस लौटाता है।

Note

ध्यान दीजिए कि check_password फंक्शन को इतना सरल बनाया जा सकता है:

check_password () { [[ $1 == "secret" ]]; }
  1. [[...]] शर्त वाली रचना का भी एक एग्ज़िट स्टेटस होता है: सही के लिए 0, और गलत के लिए 1।
  2. {...} समूह बनाने वाली रचना में बंद करने वाले ब्रैकेट से पहले नई लाइन या सेमीकोलन ज़रूरी है।

फंक्शन का आउटपुट

फंक्शन का रिटर्न स्टेटस बस एक संख्या है। तो फंक्शन आउटपुट कैसे दे सकता है?

आपका फंक्शन स्टैंडर्ड आउटपुट पर प्रिंट कर सकता है। उसे पकड़ने के लिए वही जाना-पहचाना कमांड सबस्टिट्यूशन इस्तेमाल कीजिए:

d6 () { echo "$(( 1 + RANDOM % 6 ))"; }

die=$( d6 )
echo "You rolled a $die."

आउटपुट और स्टेटस, दोनों का इस्तेमाल

जब आप फंक्शन का आउटपुट पकड़ रहे हों, तब भी उसका एग्ज़िट स्टेटस इस्तेमाल के लिए उपलब्ध रहता है।

roll () {
    local n=$1
    if (( 4 <= n && n <= 20 )); then
        echo "$(( 1 + RANDOM % n ))"  # exit status is 0
    else
        return 1
    fi
}

read -p "How many faces does your die have? " faces
if die=$( roll "$faces" ); then
    echo "You rolled a $die."
else
    echo "I can't roll a die with $faces faces."
fi

रिकर्शन

फंक्शन खुद को रिकर्शन के ज़रिए कॉल कर सकते हैं। डिफॉल्ट रूप से रिकर्शन की गहराई की कोई सीमा नहीं होती।

एक उदाहरण:

fibonacci() {
    local n=$1
    if (( n <= 1 )); then
        echo "1"
    else
        local a=$(fibonacci "$(( n - 1 ))")
        local b=$(fibonacci "$(( n - 2 ))")
        echo "$(( a + b ))"
    fi
}

for i in {1..10}; do fibonacci "$i"; done
# => 1
# => 2
# => 3
# => 5
# => 8
# => 13
# => 21
# => 34
# => 55
# => 89
Advanced

रिकर्शन की गहराई को FUNCNEST वेरिएबल से नियंत्रित किया जा सकता है।

bash -c '
    recur() {
        echo $1
        recur $(($1 + 1))
    }
    FUNCNEST=5
    recur 1
'
1
2
3
4
5
environment: line 1: recur: maximum function nesting level exceeded (5)
GitHub के ज़रिए संपादित करें यह लिंक नई विंडो या टैब में खुलता है।