অনেক 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" ]]; }
[[...]] কন্ডিশনাল কনস্ট্রাক্টের একটি এক্সিট স্ট্যাটাস থাকে: "true"-এর জন্য 0, "false"-এর জন্য 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)