Linux: أساسيات سكريبت Bash — المتغيرات / الشروط / الحلقات

احفظ سلسلة من أوامر لينكس في ملف، وأضف بعض التحكم المنطقي (شروط، حلقات)، فتحصل على سكريبت صدفة (Shell script). هذه هي نقطة البداية لأتمتة كل العمل المتكرر.

📋 المتطلبات المسبقة: يجب أن تعرف بالفعل

1. ما ستتعلمه


2. قصة من النشر اليدوي إلى النشر بنقرة واحدة

(1) نقطة الألم: كتابة نفس الأوامر الخمسة كل يوم

كان شياو مينغ يُنفّذ هذه الأوامر كل يوم للنشر: git pull، وnpm install، وnpm run build، وpm2 restart app، وecho "done". 5 أوامر، 3 دقائق، متكررة يوميًا.

(2) سكريبت من 8 أسطر

كتب deploy.sh:

BASH
#!/bin/bash
set -e
echo "Starting deployment..."

cd /var/www/myapp
git pull origin main
npm install
npm run build
pm2 restart app
echo "✅ Deployment complete!"
💡 نصيحة: set -e هو خط الدفاع الأول لسلامة السكريبت — السكريبت يتوقف فورًا إذا أعاد أي أمر رمز خروج غير صفري. بدون set -e، يستمر السكريبت حتى بعد فشل أمر، مما قد يسبب أخطاء متسلسلة خطيرة (مثل إعادة تشغيل خدمة بكود معطوب).

(3) الفائدة: نشر بنقرة واحدة

منذ ذلك الحين، صار شياو مينغ يحتاج فقط تشغيل ./deploy.sh — كل الخطوات تُنجز تلقائيًا، موفرًا الوقت ومتجنبًا النسيان.


3. نقاط المعرفة

(1) Shebang

السطر الأول من السكريبت يجب أن يكون Shebang — يخبر النظام أي مفسر يستخدم:

TEXT
#!/bin/bash      # التنفيذ بـbash
#!/bin/sh        # التنفيذ بـsh (وضع التوافق)
#!/usr/bin/env python3  # التنفيذ بـPython 3
💡 نصيحة: #!/bin/bash هو Shebang — يخبر نظام التشغيل أي مفسر يستخدم للسكريبت. بدون هذا السطر، يستخدم النظام الصدفة الحالية، مما قد يسبب مشاكل توافق (مثل أن sh لا يدعم صياغة [[ ]]). كل سكريبتات bash يجب أن تبدأ بـ#!/bin/bash.

(2) المتغيرات

TEXT
# تعريف متغيرات (بدون مسافات حول علامة التساوي)
name="Alice"
age=25

# استخدام المتغيرات (الأقواس المعقوفة مُوصى بها)
echo $name          # Alice
echo ${name}        # Alice (الأقواس أأمن)
echo "Hello, ${name}"

# قواعد تسمية المتغيرات
# 1. أحرف، أرقام، شرطات سفلية
# 2. لا يمكن أن تبدأ برقم
# 3. حساسة لحالة الأحرف (NAME != name)

ثلاثة أنواع من علامات الاقتباس:

علامة الاقتباس مثال السلوك
علامات مزدوجة "Hello $name" تحليل المتغيرات والأوامر
علامات مفردة 'Hello $name' نص حرفي، بدون تحليل
علامات خلفية/$() `date` أو $(date) تنفيذ أمر، التقاط المخرجات

ℹ️ ملاحظة: الفرق الجوهري بين علامات الاقتباس المفردة والمزدوجة — في المزدوجة، يُحلّل $variable و$(command) ويُستبدل؛ كل شيء في المفردة حرفي. استخدم المزدوجة عند دمج المتغيرات، والمفردة عندما تحتاج حفظ $ وأحرف خاصة أخرى (مثل أنماط regex أو heredocs حيث لا تريد توسيع المتغيرات).

TEXT
name="Alice"
echo "Hello $name"     # Hello Alice
echo 'Hello $name'     # Hello $name
echo "Today is $(date)"    # Today is Tue Jul  7 10:23:45 CST 2026

(3) المعاملات الموضعية

TEXT
./script.sh arg1 arg2 arg3
#           $1    $2    $3

$0       # اسم السكريبت نفسه
$1, $2   # المعامل الأول، المعامل الثاني
$#       # عدد المعاملات
$@       # كل المعاملات (ككلمات منفصلة)
$*       # كل المعاملات (كنص واحد)
$?       # رمز خروج الأمر السابق (0=نجاح، غير صفري=فشل)
$$       # PID السكريبت الحالي

(4) المنطق الشرطي (if / test / [[ ]])

TEXT
# أمر test أو [ ]
if [ "$name" = "Alice" ]; then
    echo "Hello Alice"
fi

# [[ ]] الأقوى (خاص بـbash)
if [[ "$name" == "Alice" ]]; then
    echo "Hello Alice"
fi

# if-elif-else
if [ "$1" -gt 10 ]; then
    echo "Greater than 10"
elif [ "$1" -gt 5 ]; then
    echo "Greater than 5"
else
    echo "5 or less"
fi

اختبارات الملفات:

الشرط المعنى
-f file ملف عادي
-d dir دليل
-e path موجود
-s file حجم الملف > 0
-r file قابل للقراءة
-w file قابل للكتابة
-x file قابل للتنفيذ

مقارنة النصوص:

الشرط المعنى
$a = $b متساوي
$a != $b غير متساوي
-z $a النص فارغ (الطول = 0)
-n $a النص غير فارغ

المقارنة العددية:

الشرط المعنى
$a -eq $b يساوي
$a -ne $b لا يساوي
$a -gt $b أكبر من
$a -ge $b أكبر من أو يساوي
$a -lt $b أصغر من
$a -le $b أصغر من أو يساوي

(5) الحلقات

BASH
# حلقة for
for i in 1 2 3 4 5; do
    echo "Number: $i"
done

for file in *.txt; do
    echo "Found file: $file"
done

for i in {1..10}; do
    echo "$i"
done

# حلقة while
count=1
while [ $count -le 5 ]; do
    echo "Attempt $count"
    count=$((count + 1))
done

# قراءة ملف سطرًا بسطر
while IFS= read -r line; do
    echo "$line"
done < /etc/hostname

(6) ▶ مثال: سكريبت أساسي

BASH
#!/bin/bash
# hello.sh - أول سكريبت bash
echo "Hello, World!"
echo "Current user: $(whoami)"
echo "Current directory: $(pwd)"
echo "File count: $(ls | wc -l)"

الإخراج:

TEXT
Hello, World!
Current user: alice
Current directory: /home/alice
File count: 12

(7) ▶ مثال: استخدام المعاملات الموضعية

TEXT
#!/bin/bash
# greet.sh - تحية مستخدم محدد
if [ $# -eq 0 ]; then
    echo "Usage: $0 <username>"
    exit 1
fi

echo "Hello, $1!"
echo "Welcome to Linux scripting."
echo "Script name: $0"
echo "Argument count: $#"
TEXT
./greet.sh Alice
# Hello, Alice!

(8) ▶ مثال: شرط if

TEXT
#!/bin/bash
# check-file.sh - فحص حالة ملف

FILE="${1:-test.txt}"

if [ ! -e "$FILE" ]; then
    echo "File '$FILE' does not exist"
    exit 1
fi

if [ -f "$FILE" ]; then
    echo "Type: Regular file"
elif [ -d "$FILE" ]; then
    echo "Type: Directory"
fi

[ -r "$FILE" ] && echo "Readable: ✅" || echo "Readable: ❌"
[ -w "$FILE" ] && echo "Writable: ✅" || echo "Writable: ❌"
[ -x "$FILE" ] && echo "Executable: ✅" || echo "Executable: ❌"

(9) ▶ مثال: عملية دفعية بحلقة for

TEXT
#!/bin/bash
# rename.sh - إعادة تسمية دفعية من .txt إلى .md

count=0
for file in *.txt; do
    if [ -f "$file" ]; then
        mv "$file" "${file%.txt}.md"
        echo "Renamed: $file → ${file%.txt}.md"
        count=$((count + 1))
    fi
done

echo "Total files renamed: $count"

(10) ▶ مثال: حلقة while وقراءة المدخلات

TEXT
#!/bin/bash
# countdown.sh - عدّ تنازلي

if [ $# -ne 1 ]; then
    echo "Usage: $0 <seconds>"
    exit 1
fi

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

echo "Time's up!"

(11) ▶ مثال شامل: سكريبت معالجة دفعية للملفات

BASH
#!/bin/bash
# batch-process.sh - معالجة دفعية لملفات في دليل

DIR="${1:-.}"
ACTION="${2:-info}"

if [ ! -d "$DIR" ]; then
    echo "Error: Directory '$DIR' does not exist"
    exit 1
fi

echo "Processing directory: $DIR"
echo "Action: $ACTION"
echo "---"

case "$ACTION" in
    info)
        echo "File statistics:"
        echo "  Total files: $(find "$DIR" -type f | wc -l)"
        echo "  Total directories: $(find "$DIR" -type d | wc -l)"
        echo "  Total size: $(du -sh "$DIR" | cut -f1)"
        ;;
    backup)
        BACKUP="/tmp/backup-$(date +%Y%m%d)"
        mkdir -p "$BACKUP"
        cp -r "$DIR" "$BACKUP/"
        echo "Backup complete: $BACKUP"
        ;;
    clean)
        echo "Cleaning .log files..."
        find "$DIR" -name "*.log" -delete
        echo "Cleaning .tmp files..."
        find "$DIR" -name "*.tmp" -delete
        echo "Cleaning complete"
        ;;
    *)
        echo "Actions: info / backup / clean"
        ;;
esac

❓ أسئلة شائعة

س: هل #!/bin/bash في السطر الأول إجباري؟ ج: نعم. يخبر النواة أي مفسر تستخدم. بدونه، الافتراضي هو الصدفة الحالية (مما قد يسبب مشاكل توافق).

س: لماذا يقول سكريبتي command not found؟ ج: سببان: 1) لا توجد صلاحية تنفيذ — شغّل chmod +x script.sh. 2) الدليل الحالي ليس في PATH — استخدم ./script.sh بدلًا من script.sh.

س: ما الفرق بين $@ و$*؟ ج: $@ يعامل كل معامل كـ"كلمة" منفصلة؛ و$* يجمع كل المعاملات في نص واحد. معظم السيناريوهات توصي بـ$@. التكرار بـfor arg in "$@"; do هو الممارسة القياسية.

س: ما الفرق بين exit 0 وexit 1؟ ج: exit 0 يعني نجاح؛ وexit non-zero يعني فشل (أرقام مختلفة تدل على أنواع أخطاء مختلفة). البرامج الأخرى يمكنها فحص $? لمعرفة هل نجح السكريبت.

س: كيف أحصل على مدخلات المستخدم في سكريبت؟ ج: استخدم أمر read: read -p "Enter your name: " name، ثم $name يحتوي على مدخلات المستخدم.


📖 ملخص


📝 تمارين

  1. أساسي (⭐): اكتب سكريبت info.sh يُخرج اسم المستخدم الحالي، والتاريخ، والدليل الحالي، وعدد الملفات
  2. متوسط (⭐⭐): اكتب سكريبت check-file.sh يأخذ اسم ملف كمعامل، ويتحقق من وجوده ويُخرج نوعه. ثم اكتب سكريبت backup-txt.sh ينسخ كل ملفات .txt احتياطيًا من الدليل الحالي إلى /tmp/txt-backup/
  3. متقدم (⭐⭐⭐): اكتب سكريبت عدّ تنازلي يأخذ عدد ثوانٍ كمعامل ويعدّ تنازليًا كل ثانية، وأضف معالجة أخطاء set -e. ثم استخدم حلقة for لإنشاء 10 أدلة باسم test-1 حتى test-10، وأخيرًا اكتب سكريبتًا يحسب ويرتّب أحجام تلك الأدلة العشرة
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%