هر اسکریپتی که بخواهد فراتر از یک دنبالهی خطی از دستورات عمل کند، به ساختارهای شرطی نیاز دارد. Bash ابزارهای متنوعی برای آزمودن شرایط، مقایسهی رشتهها و اعداد، بررسی وضعیت فایلها، و ترکیب چند شرط در اختیار قرار میدهد. در این درس، تمام این ابزارها را با جزئیات کامل پوشش میدهیم.
دستور test، پرانتزهای [ ] و [[ ]]
در Bash، شرطها در هستهی خود از دستور test مشتق شدهاند. این دستور یک عبارت را ارزیابی میکند و در صورت درستی، کد خروج 0 (موفقیت) و در غیر این صورت 1 (شکست) برمیگرداند.
سه روش برای نوشتن شرط در Bash وجود دارد:
| روش | توضیح | کاربرد پیشنهادی |
|---|---|---|
test expression | دستور مستقل قدیمی | کمتر در اسکریپتهای مدرن استفاده میشود |
[ expression ] | نسخهی مخفف test؛ مطابق استاندارد POSIX | برای اسکریپتهای قابلحمل (POSIX) |
[[ expression ]] | نسخهی پیشرفتهی Bash (keyword، نه built-in) | روش ترجیحی در اسکریپتهای Bash |
چرا
[[ ]]بر[ ]ارجحیت دارد؟
در
[[ ]]، تقسیم کلمه (word splitting) و گلوبینگ (globbing) انجام نمیشود، بنابراین نیازی به گیومهگذاری متغیرها نیست (اگرچه همچنان توصیه میشود).از عملگرهای
&&،||،<و>بهعنوان عملگر منطقی و مقایسهای درون خود شرط پشتیبانی میکند (در[ ]باید از-a،-oیا استفادهی جداگانه استفاده کرد).از الگوی گلوبینگ (مثل
*.txt) و عبارات منظم (با=~) پشتیبانی میکند.
ساختارهای شرطی: if، elif و else
ساختار پایهی شرطی در Bash به این شکل است:
if [[ condition ]]; then # در صورت درستی شرط elif [[ another_condition ]]; then # در صورت درستی شرط دوم else # در صورت نادرستی تمام شرایط fi
نکات سینتکسی حیاتی:
-
ifوelifوelseوfiهمگی کلمات کلیدی هستند. -
نقطهویرگول (
;) قبل ازthenضروری است، مگر اینکهthenرا در خط بعدی بنویسید. -
fiنشاندهندهی پایان بلوک شرطی است.
#!/bin/bash age=18 if [[ $age -ge 18 ]]; then echo "شما بزرگسال هستید." elif [[ $age -gt 12 ]]; then echo "شما نوجوان هستید." else echo "شما کودک هستید." fi
ساختار case
وقتی میخواهید یک متغیر را با الگوهای متعدد مقایسه کنید، case جایگزین تمیزتری برای زنجیرههای طولانی elif است. سینتکس آن بر اساس تطبیق الگوی (pattern matching) کار میکند و از کاراکترهای wildcard مانند * و ? پشتیبانی میکند.
case $variable in pattern1) commands ;; pattern2|pattern3) commands ;; *) default_commands ;; esac
-
هر شاخه با دو نقطهویرگول (
;;) خاتمه مییابد که نشاندهندهی پایان آن بلوک است. -
*)معادلelseعمل میکند و همهی حالتهای دیگر را پوشش میدهد. -
میتوان چند الگو را با
|(OR) ترکیب کرد.
مثال عملی:
#!/bin/bash read -p "یک نام از اعضای تیم وارد کنید: " name case $name in "Ali"|"Reza") echo "عضو تیم توسعه است." ;; "Sara"|"Neda") echo "عضو تیم طراحی است." ;; "Hassan") echo "مدیر پروژه است." ;; *) echo "این فرد در لیست اعضا نیست." ;; esac
مثال کاربردیتر با wildcard:
#!/bin/bash read -p "نام فایل را وارد کنید: " filename case $filename in *.txt|*.md) echo "یک فایل متنی است." ;; *.jpg|*.png|*.gif) echo "یک فایل تصویری است." ;; *.sh) echo "یک اسکریپت Bash است." ;; *) echo "نوع فایل ناشناخته است." ;; esac
آزمونهای رشتهای (String Tests)
برای مقایسه و بررسی رشتهها در [[ ]] از عملگرهای زیر استفاده میشود:
| عملگر | توضیح | مثال |
|---|---|---|
=~ | تطبیق با عبارت منظم (Regex) | [[ "text" =~ ^t ]] |
== | برابری (مقایسهی دقیق) | [[ "$name" == "Ali" ]] |
= | برابری (معادل == در [[ ]]) | [[ "$name" = "Ali" ]] |
!= | نابرابری | [[ "$name" != "Ali" ]] |
> | بزرگتر بودن (بر اساس ترتیب الفبایی در locale فعلی) | [[ "b" > "a" ]] |
< | کوچکتر بودن (بر اساس ترتیب الفبایی) | [[ "a" < "b" ]] |
-z | رشته خالی (طول صفر) | [[ -z "$var" ]] |
-n | رشته غیرخالی (طول غیرصفر) | [[ -n "$var" ]] |
مثالهای عملی:
#!/bin/bash str1="hello" str2="world" empty="" # بررسی برابری if [[ "$str1" == "hello" ]]; then echo "str1 برابر با hello است." fi # بررسی خالی بودن if [[ -z "$empty" ]]; then echo "متغیر empty خالی است." fi # بررسی غیرخالی بودن if [[ -n "$str2" ]]; then echo "str2 خالی نیست." fi # مقایسهی الفبایی if [[ "apple" < "banana" ]]; then echo "apple از banana کوچکتر است." fi
عبارات منظم با =~:
#!/bin/bash email="user@example.com" if [[ "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then echo "ایمیل معتبر است." fi
در
[[ ]]، اگر سمت راست عملگر==را بدون گیومه بنویسید، بهعنوان الگوی گلوبینگ (wildcard) تفسیر میشود. مثلاً[[ $file == *.txt ]]بررسی میکند که آیا$fileبه.txtختم میشود یا خیر.
آزمونهای عددی (Numeric Tests)
برای مقایسهی اعداد صحیح، دو روش اصلی وجود دارد:
روش اول: استفاده از عملگرهای (( ... )) (روش ترجیحی و خواناتر)
درون (( ))، از عملگرهای معمول ریاضی استفاده میشود: >, <, >=, <=, ==, !=.
#!/bin/bash a=10 b=20 if (( a > b )); then echo "a بزرگتر است." elif (( a < b )); then echo "b بزرگتر است." else echo "a و b برابرند." fi
روش دوم: استفاده از پرانتز یا test با فلگهای مخصوص
در [ ] یا [[ ]] برای اعداد از این عملگرها استفاده میشود:
| عملگر | توضیح |
|---|---|
-eq | برابر (Equal) |
-ne | نابرابر (Not Equal) |
-lt | کوچکتر از (Less Than) |
-le | کوچکتر یا برابر (Less or Equal) |
-gt | بزرگتر از (Greater Than) |
-ge | بزرگتر یا برابر (Greater or Equal) |
#!/bin/bash score=85 if [[ $score -ge 90 ]]; then grade="A" elif [[ $score -ge 80 ]]; then grade="B" elif [[ $score -ge 70 ]]; then grade="C" else grade="F" fi echo "نمره: $grade"
نکتهی مهم:
(( ))از اعداد اعشاری (float) پشتیبانی نمیکند. برای محاسبات اعشاری باید از ابزارهایی مثلbcیاawkاستفاده کنید.
آزمونهای فایل (File Tests)
Bash مجموعهی کاملی از عملگرها برای بررسی ویژگیهای فایلها و دایرکتوریها ارائه میدهد. این آزمونها در [ ] و [[ ]] به یک شکل کار میکنند.
| عملگر | توضیح | مثال |
|---|---|---|
-e | فایل یا دایرکتوری وجود دارد (exists) | [[ -e /etc/passwd ]] |
-f | یک فایل معمولی (regular file) است | [[ -f config.conf ]] |
-d | یک دایرکتوری است | [[ -d /home ]] |
-L | یک لینک سمبلیک است | [[ -L /usr/bin/python ]] |
-h | معادل -L | [[ -h /usr/bin/python ]] |
-s | فایل غیرخالی است (حجم > ۰) | [[ -s log.txt ]] |
-r | فایل قابل خواندن است (permission read) | [[ -r secret.txt ]] |
-w | فایل قابل نوشتن است (permission write) | [[ -w config.ini ]] |
-x | فایل قابل اجرا است (permission execute) | [[ -x script.sh ]] |
-O | مالکیت فایل متعلق به کاربر فعلی است | [[ -O myfile ]] |
-G | گروه فایل با گروه کاربر فعلی یکسان است | [[ -G myfile ]] |
-nt | فایل اول جدیدتر از فایل دوم است (newer than) | [[ file1 -nt file2 ]] |
-ot | فایل اول قدیمیتر از فایل دوم است (older than) | [[ file1 -ot file2 ]] |
-ef | دو فایل به یک inode اشاره میکنند (لینکهای سخت یکسان) | [[ file1 -ef file2 ]] |
مثال عملی جامع:
#!/bin/bash file="/var/log/syslog" if [[ ! -e "$file" ]]; then echo "خطا: فایل وجود ندارد!" exit 1 elif [[ -f "$file" && -s "$file" ]]; then echo "فایل موجود است و خالی نیست." elif [[ -f "$file" && ! -s "$file" ]]; then echo "فایل موجود است اما خالی است." fi if [[ -r "$file" && -w "$file" ]]; then echo "شما دسترسی خواندن و نوشتن به این فایل دارید." elif [[ -r "$file" && ! -w "$file" ]]; then echo "شما فقط دسترسی خواندن دارید." fi
عملگرهای منطقی (Logical Operators)
برای ترکیب چند شرط از عملگرهای منطقی استفاده میشود. رفتار این عملگرها بسته به اینکه درون [[ ]] باشند یا بین چند دستور، تفاوت دارد.
درون [[ ]]
| عملگر | توضیح |
|---|---|
&& | و (AND) — هر دو شرط باید درست باشند |
|| | یا (OR) — حداقل یکی از دو شرط درست باشد |
! | نقیض (NOT) — شرط را معکوس میکند |
#!/bin/bash age=25 name="Ali" if [[ $age -gt 18 && "$name" == "Ali" ]]; then echo "Ali بزرگسال است." fi if [[ $age -lt 18 || $age -gt 60 ]]; then echo "مشمول تخفیف ویژه است." fi if [[ ! -f "config.txt" ]]; then echo "فایل config.txt وجود ندارد!" fi
بین دستورات (در سطح شل)
وقتی از پرانتزهای تکی [ ] استفاده میکنید یا میخواهید چند دستور مستقل را شرطی کنید، از && و || در سطح شل استفاده میشود:
# اجرای دستور دوم فقط در صورت موفقیت دستور اول mkdir new_dir && cd new_dir # اجرای دستور دوم فقط در صورت شکست دستور اول command1 || echo "command1 با خطا مواجه شد" # ترکیب با پرانتزهای تکی (روش قدیمی) if [ -f file1.txt ] && [ -f file2.txt ]; then echo "هر دو فایل وجود دارند." fi
در
[[ ]]، عملگرهای&&و||درون خود پرانتز تفسیر میشوند و تقدم (precedence) مشخصی دارند. برای شفافیت، از پرانتزهای اضافی استفاده کنید:if [[ ($age -gt 18 && "$city" == "Tehran") || "$role" == "admin" ]]; then echo "دسترسی تایید شد." fi
جمعبندی نهایی: انتخاب ابزار مناسب
| سناریو | ابزار/ساختار پیشنهادی |
|---|---|
| مقایسهی اعداد صحیح | (( a > b )) |
| مقایسهی رشتهها (با دقت بالا) | [[ "$str" == "value" ]] |
| بررسی خالی بودن رشته | [[ -z "$var" ]] |
| بررسی ویژگیهای فایل | [[ -f "/path" ]] |
تطبیق الگوی wildcard (مثل *.txt) | [[ "$file" == *.txt ]] (بدون گیومه برای الگو) |
| تطبیق با عبارت منظم (Regex) | [[ "$text" =~ ^[0-9]+$ ]] |
| چندین شاخهی شرطی بر اساس یک مقدار | case $var in ... esac |
| ترکیب چند شرط پیچیده | [[ (cond1) && (cond2) || (cond3) ]] |