مقدمه

با یادگیری متغیرها، شرط‌ها و حلقه‌ها، ما ابزارهای لازم برای نوشتن اسکریپت‌های قدرتمند را در اختیار داریم. اما با بزرگتر و پیچیده‌تر شدن اسکریپت‌ها، مدیریت آن‌ها نیز دشوارتر می‌شود. اگر یک قطعه کد خاص وجود داشته باشد که لازم باشد آن را در چندین جای مختلف از اسکریپت خود اجرا کنیم، آیا باید آن را هر بار کپی و پیست کنیم؟ این کار نه تنها اسکریپت را طولانی و ناخوانا می‌کند، بلکه نگهداری آن را نیز به یک کابوس تبدیل می‌کند.

راه حل این مشکل، استفاده از توابع (Functions) است. یک تابع، یک بلوک نام‌گذاری شده از کد است که یک وظیفه‌ی خاص را انجام می‌دهد و ما می‌توانیم آن را از هر جای اسکریپت خود با صدا زدن نامش، اجرا کنیم. استفاده از توابع سه مزیت اصلی دارد:

  • استفاده مجدد (Reusability): کد را یک بار می‌نویسید و بارها از آن استفاده می‌کنید. (اصل برنامه‌نویسی DRY: Don't Repeat Yourself)
  • خوانایی (Readability): با شکستن یک اسکریپت بزرگ به توابع کوچک و منطقی، درک عملکرد کلی آن بسیار آسان‌تر می‌شود.
  • نگهداری آسان (Maintainability): اگر نیاز به تغییر یا رفع اشکال در یک بخش از کد داشته باشید، فقط کافیست آن را در تعریف تابع مربوطه اصلاح کنید.

تعریف و فراخوانی یک تابع

تعریف یک تابع در شل بسیار ساده است. ما یک نام برای تابع خود انتخاب کرده و سپس دستورات مورد نظر را درون آکولاد {} قرار می‌دهیم.

function_name() {
  echo "Hello from the function!"
}

توجه کنید که تعریف کردن یک تابع، آن را اجرا نمی‌کند. برای اجرای کدهای درون تابع، باید آن را فراخوانی (call) کنیم. فراخوانی یک تابع نیز به سادگی تایپ کردن نام آن است، درست مانند یک دستور عادی.

#!/bin/bash

log_message() {
  echo "$(date): an important message"
}

echo "starting script..."
# calling the function
log_message
echo "script continues..."
log_message # calling again
echo "script finished."

ارسال آرگومان به توابع

توابع زمانی بسیار مفیدتر می‌شوند که بتوانند مقادیر ورودی دریافت کنند. این مقادیر ورودی آرگومان (Arguments) نامیده می‌شوند. توابع در شل، آرگومان‌های خود را دقیقاً مانند خود اسکریپت، از طریق پارامترهای موقعیتی $1 (اولین آرگومان)، $2 (دومین آرگومان) و الی آخر دریافت می‌کنند.

#!/bin/bash

greet() {
  echo "Hello، $1!"
}

greet "Ali"
greet "Mina"

بازگرداندن مقدار از توابع

دو روش اصلی برای «بازگرداندن» نتیجه از یک تابع وجود دارد.

۱. بازگرداندن کد خروج (Exit Code)

توابع نیز مانند دستورات عادی می‌توانند یک کد خروج (عددی بین ۰ تا ۲۵۵) را با استفاده از دستور return برگردانند. این روش برای نشان دادن وضعیت موفقیت (return 0) یا شکست (return 1) یک تابع عالی است.

#!/bin/bash
is_root() {
  if [[ "$(id -u)" -eq 0 ]]; then
    return 0  
  else
    return 1 
  fi
}

if is_root; then
  echo "You are running as root."
else
  echo "You are running as a regular user."
fi

۲. بازگرداندن متن (رشته)

اگر بخواهید یک مقدار متنی (مانند یک نام فایل یا نتیجه یک محاسبه) را از یک تابع برگردانید، نباید از return استفاده کنید. در عوض، تابع شما باید آن مقدار را با دستور echo چاپ کند. سپس، کدی که تابع را فراخوانی کرده، می‌تواند این خروجی را با استفاده از جایگزینی دستور $(...) دریافت و در یک متغیر ذخیره کند.

#!/bin/bash

get_kernel_version() {
  uname -r
}

kernel_v=$(get_kernel_version)

echo "Your kernel version is: $kernel_v"

حوزه‌ی متغیرها: local در برابر global

به طور پیش‌فرض، تمام متغیرهایی که در یک اسکریپت شل تعریف می‌کنید، عمومی (global) هستند. این یعنی از هر جایی در اسکریپت، چه داخل توابع و چه خارج از آن‌ها، قابل دسترسی و تغییر هستند. این موضوع می‌تواند خطرناک باشد، زیرا یک تابع ممکن است به صورت ناخواسته مقدار متغیری را که در جای دیگری از اسکریپت استفاده می‌شود، تغییر دهد و باعث بروز خطاهای پنهان شود.

برای جلوگیری از این مشکل، بهترین کار این است که متغیرهایی که فقط در داخل یک تابع استفاده می‌شوند را با کلمه‌ی کلیدی local تعریف کنیم. یک متغیر local فقط در همان تابعی که تعریف شده قابل مشاهده است و با پایان یافتن اجرای تابع، از بین می‌رود.

#!/bin/bash
create_message() {
  local name="$1"
  local message="Hello, $name"
  echo "$message"
}

message="This is a global variable."
returned_message=$(create_message "Sara")

echo "Message from function: $returned_message"
echo "Global message is still: $message"