Bash for Oracle DBAs قسمت هشتم Error Handling حرفه‌ای در Bash

تا اینجا یاد گرفتیم:

  • شرط‌ها
  • حلقه‌ها
  • توابع
  • پردازش متن

اما هنوز یک مشکل بزرگ باقی مانده است.

بسیاری از اسکریپت‌های Bash در محیط production به‌درستی خطاها را مدیریت نمی‌کنند.

در نتیجه ممکن است:

  • بخشی از اسکریپت fail شود
  • اما اسکریپت ادامه پیدا کند
  • و نتیجه نهایی اشتباه باشد

برای جلوگیری از این مشکل باید Error Handling را جدی بگیریم.


مفهوم Exit Code در لینوکس

هر دستور در لینوکس یک کد خروج (Exit Status) برمی‌گرداند.

قاعده کلی:

0   → موفق
غیر صفر → خطا

مثال:

ls /etc/passwd
echo $?

اگر فایل وجود داشته باشد:

0

اگر وجود نداشته باشید:

2

متغیر $? همیشه exit code آخرین دستور را نگه می‌دارد.


استفاده از Exit Code در شرط‌ها

مثال:

cp file1 file2

if [[ $? -ne 0 ]]; then
  echo "Copy failed"
fi

اما این روش خیلی تمیز نیست.

روش بهتر این است که مستقیماً دستور را در if قرار دهیم.

✅ بهتر:

if cp file1 file2; then
  echo "Copy successful"
else
  echo "Copy failed"
fi

گزینه مهم: set -e

یکی از مهم‌ترین تنظیمات برای اسکریپت‌ها:

set -e

معنی آن:

اگر هر دستور خطا بدهد، اسکریپت فوراً متوقف می‌شود.

مثال:

set -e

cp file1 file2
rm file3

اگر cp شکست بخورد، دستور rm اجرا نمی‌شود.

این کار از بسیاری از خطاهای خطرناک جلوگیری می‌کند.


گزینه مهم: set -u

set -u

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

مثال:

echo $username

اگر username تعریف نشده باشد، اسکریپت fail می‌شود.

این کار از باگ‌های پنهان جلوگیری می‌کند.


گزینه مهم: pipefail

در pipelineها مشکل رایجی وجود دارد.

مثال:

grep ERROR file.log | sort

اگر grep خطا بدهد اما sort موفق باشد، exit code برابر 0 می‌شود.

برای جلوگیری از این مشکل:

set -o pipefail

در این حالت اگر هر بخش pipeline خطا بدهد، اسکریپت fail می‌شود.


الگوی استاندارد حرفه‌ای

در ابتدای اسکریپت‌های حرفه‌ای معمولاً این خط قرار می‌گیرد:

set -euo pipefail

معنی آن:

  • -e توقف در صورت خطا
  • -u خطا در صورت متغیر تعریف‌نشده
  • pipefail تشخیص خطای pipeline

این یکی از بهترین practiceها در Bash scripting است.


مدیریت خطا در توابع

می‌توانیم در توابع هم خطاها را بررسی کنیم.

مثال:

check_file() {
  local file=$1

  if [[ ! -f "$file" ]]; then
    echo "File not found: $file"
    return 1
  fi

  echo "File exists"
}

استفاده:

if ! check_file "/etc/passwd"; then
  exit 1
fi

مثال واقعی: بررسی backup

فرض کنید می‌خواهیم بررسی کنیم فایل backup وجود دارد یا نه.

#!/bin/bash

set -euo pipefail

backup="/backup/db.bkp"

if [[ ! -f "$backup" ]]; then
  echo "Backup file not found"
  exit 1
fi

echo "Backup file exists"

اگر backup وجود نداشته باشد اسکریپت با خطا خارج می‌شود.


ثبت خطا در log

در اسکریپت‌های واقعی بهتر است خطاها log شوند.

مثال:

log() {
  echo "[$(date)] $1"
}

if ! systemctl is-active --quiet sshd; then
  log "sshd service is not running"
fi

اشتباه رایج

❌ نادیده گرفتن خطاها:

cp file1 file2
rm file3

اگر cp fail شود ممکن است rm فایل مهمی را حذف کند.

✅ بهتر:

set -euo pipefail

تمرین

  1. اسکریپتی بنویسید که بررسی کند یک دایرکتوری وجود دارد.
  2. اگر وجود نداشت پیام خطا چاپ کند و exit کند.
  3. اگر وجود داشت تعداد فایل‌ها را چاپ کند.