बहुत-सी Bash स्क्रिप्ट सीधे आदेश देने वाली शैली में लिखी जाती हैं: पहले एक कमांड चलाइए, फिर दूसरी, और इसी तरह। कभी-कभी आपको कई कमांडों को एक साथ इस तरह रखना पड़ता है कि वे मिलकर एक ही उद्देश्य पूरा करें। यहीं फंक्शन काम आते हैं।
फंक्शन बनाने का तरीका यह है:
my_function () {
COMMANDS
}
गोल ब्रैकेट का खाली जोड़ा बस यह बताता है कि आप एक फंक्शन बना रहे हैं। इनके अंदर कुछ नहीं लिखा जाता।
एक बार बन जाने के बाद फंक्शन किसी भी दूसरी कमांड की तरह काम करते हैं (चाहे वह बिल्ट-इन हो या न हो)।
किसी भी कमांड की तरह आप अपने फंक्शन को आर्गुमेंट दे सकते हैं।
फंक्शन के अंदर आप इन आर्गुमेंटों तक पोज़िशनल पैरामीटर, $1, $2 आदि के ज़रिए पहुँचते हैं।
(याद कीजिए, हमने पोज़िशनल पैरामीटर के बारे में वेरिएबल कॉन्सेप्ट में पढ़ा था।)
फंक्शन के अंदर विशेष पैरामीटर $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
फंक्शन के अंदर आप कॉलर के स्कोप के वेरिएबलों तक पहुँच सकते हैं। यानी आप ग्लोबल वेरिएबल इस्तेमाल कर सकते हैं, और वे लोकल वेरिएबल भी जो कॉलर में (या किसी ऐसे फंक्शन में जो कॉलर को कॉल करता है) घोषित हुए थे।
तकनीकी रूप से देखें तो "ग्लोबल" सही शब्द नहीं है। फंक्शन में किसी वेरिएबल की वैल्यू निकालने के लिए 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
इसी तरह, किसी वेरिएबल को वैल्यू असाइन करने का मतलब है उसे उसी स्कोप में असाइन करना जहाँ वह घोषित हुआ था। यह "दूर से की गई कार्रवाई" ऐसा कोड बना सकती है जिसे समझना मुश्किल हो, क्योंकि यह हमेशा साफ नहीं होता कि किसी वेरिएबल को वैल्यू कहाँ असाइन हुई थी।
कॉल स्टैक को 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 को बिना किसी आर्गुमेंट के इस्तेमाल करने पर वह सबसे अंत में चली गई कमांड का स्टेटस लौटाता है।
ध्यान दीजिए कि check_password फंक्शन को इतना सरल बनाया जा सकता है:
check_password () { [[ $1 == "secret" ]]; }
[[...]] शर्त वाली रचना का भी एक एग्ज़िट स्टेटस होता है: सही के लिए 0, और गलत के लिए 1।{...} समूह बनाने वाली रचना में बंद करने वाले ब्रैकेट से पहले नई लाइन या सेमीकोलन ज़रूरी है।फंक्शन का रिटर्न स्टेटस बस एक संख्या है। तो फंक्शन आउटपुट कैसे दे सकता है?
आपका फंक्शन स्टैंडर्ड आउटपुट पर प्रिंट कर सकता है। उसे पकड़ने के लिए वही जाना-पहचाना कमांड सबस्टिट्यूशन इस्तेमाल कीजिए:
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
रिकर्शन की गहराई को 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)