پرش به محتویات

Customize or write simple scripts

هدف درس

در این درس نوشتن و سفارشی‌سازی اسکریپت‌های سادهٔ Bash را می‌آموزید: ساختار شل، متغیرها، آرگومان‌ها، جانشینی دستور (command substitution)، شرط‌ها و حلقه‌ها، اجرای اسکریپت و مدیریت مالکیت و مجوزها.

دانش مورد نیاز

  • آشنایی با دستورات پایهٔ شل
  • آشنایی با ویرایشگر متن و مجوزهای فایل

نکات کلیدی (اهداف)

  • استفاده از سینتکس استاندارد sh (شرط‌ها، حلقه‌ها، تست‌ها)
  • استفاده از جانشینی دستور ($(command) یا `command`)
  • بررسی مقدار بازگشتی برنامه‌ها برای موفقیت/خطا ($? و exit)
  • اجرای دستورات در یک خط با ;, &&, ||
  • ارسال ایمیل شرطی به سوپریوزر
  • انتخاب صحیح مفسر با shebang (#!)
  • مدیریت مکان، مالکیت، اجرا و SUID اسکریپت‌ها

اصطلاحات و ابزارها

for, while, test/[ ], if, read, seq, exec, ||, &&, ترکیب دستورات، mail/mailutils


اجرای چند دستور در یک خط

علامت ; دستورات را به‌صورت ترتیبی اجرا می‌کند. با && دستور دوم تنها در صورت موفقیت دستور اول اجرا می‌شود و با || زمانی اجرا می‌شود که دستور اول شکست بخورد.

مثال‌ها:

cd /tmp; ls
cp file /backups/ && rm file   # حذف فایل تنها در صورت کپی موفق
cmd1 || cmd2                   # در صورت شکست cmd1، cmd2 اجرا می‌شود

Shebang و انتخاب مفسر

اولین خط اسکریپت می‌تواند shebang باشد تا شل مناسب را مشخص کند:

#!/bin/bash

در بسیاری از سیستم‌ها #!/bin/sh نیز کار می‌کند، اما LPIC تاکید بر رفتار پایهٔ Bourne-compatible دارد.


تعریف متغیرها و آرگومان‌ها

NAME=Jadi
echo "$NAME"
echo "First arg: $1, Args count: $#"

برای دسترسی به آرگومان‌ها از $1, $2, ... و تعداد آرگومان‌ها از $# استفاده کنید.


جانشینی دستور (Command substitution)

برای گرفتن خروجی یک دستور و قرار دادن آن در متغیر از $(...) یا `...` استفاده کنید:

FILES=$(ls -1)
DATE=$(date +'%Y%m%d-%H%M')

$(...) امن‌تر و قابل تو در تو است؛ از backticks قدیمی‌تر خودداری نکنید.


اجرای اسکریپت‌ها

فایل را اجرایی کنید و سپس اجرا نمایید:

chmod +x my_script.sh
./my_script.sh

یا با sh my_script.sh آن را در زیرشل اجرا کنید. توجه: اجرای با sh یا bash باعث می‌شود اسکریپت در یک زیرشل اجرا شود و تغییرات محیطی در شل فعلی باقی نمانند.

اگر می‌خواهید شل فعلی را با برنامه جایگزین کنید از exec استفاده کنید.


شرط‌ها و تست‌ها

نمونهٔ شرط ساده:

if [ -f /etc/passwd ]; then
  echo "passwd exists"
else
  echo "no passwd"
fi

توجه کنید که test و [ ] حساس به فاصله هستند و عملگرها مانند -f, -d, -s, -x برای تست فایل‌ها مفیدند.


حلقه‌ها

مثال for و while:

for f in $(ls); do
  echo $f
done

count=5
while [ $count -gt 0 ]; do
  echo "$count"; count=$((count-1))
done

ارسال ایمیل شرطی (به root)

اگر mailutils نصب باشد می‌توانید از اسکریپت‌ها ایمیل ارسال کنید:

if ! my_backup; then
  echo "Backup failed" | mail -s "Backup failed on $(hostname)" root
fi

مقدار بازگشتی و exit

هر برنامه مقداری بازمی‌گرداند؛ 0 به‌معنی موفقیت است. بررسی $? یا استفاده از set -e و exit N کمک می‌کند تا اسکریپت رفتار قابل‌پیش‌بینی‌تری داشته باشد.


هشدارهای امنیتی و عملی (مهم)

هشدار امنیتی

  • از قرار دادن . (دایرکتوری کنونی) در ابتدای PATH خودداری کنید — این می‌تواند موجب اجرای برنامه‌های مخرب شود.
  • از eval یا اجرای رشته‌های ورودی‌ کاربران به‌طور مستقیم اجتناب کنید — خطر تزریق فرمان وجود دارد.
  • اسکریپت‌هایی که عملیات مخرب انجام می‌دهند (مثل rm, mkfs, dd) را همیشه در محیط غیرحساس تست کنید و خروجی‌ها و کدهای بازگشتی را بررسی کنید.

دربارهٔ SUID

  • قرار دادن بیت SUID روی اسکریپت‌ها معمولاً امن نیست و اکثر کرنل‌ها آن را نادیده می‌گیرند. برای نیازهای ویژه از wrapper باینری یا استفاده از sudo با تنظیمات مناسب استفاده کنید.

چک‌لیست تولید و پیاده‌سازی

  1. اسکریپت را با shellcheck یا بررسی دستی lint کنید.
  2. مجوزها را صحیح تعیین کنید (chmod, chown).
  3. تست‌های واحد و اثبات رفتار خطا (exit codes) اضافه کنید.
  4. از ثبت رخدادها (logging) و ارسال اعلان در خطای بحرانی استفاده کنید.

تمرین‌ها

  1. یک اسکریپت بکاپ ساده بنویسید که از tar استفاده کند، خروجی را بررسی کند و در صورت شکست ایمیل به root بفرستد.
  2. اسکریپتی بنویسید که آرگومان‌های خط فرمان را بررسی کند و در صورت نبودن آرگومان، پیام راهنما چاپ کند و با exit 1 خارج شود.

نکات کلیدی برای آزمون

  • بدانید چگونه shebang (#!) مفسر را مشخص می‌کند و چه تفاوتی بین #!/bin/sh و #!/bin/bash وجود دارد.
  • تشخیص دهید چگونه از && و || برای کنترل جریان استفاده کنید.
  • بدانید چگونه خروجی دستور را در متغیر ذخیره کنید ($(command)) و چگونه کد خروجی را بررسی کنید ($?, exit).