⚙️

Bash Scripting للمبتدئين: أتمتة مهام السيرفر بسكربتات عملية

تعلّم كتابة سكربتات Bash Scripting من الصفر مع أمثلة عملية لإدارة وأتمتة السيرفرات

إذا كنت تدير سيرفر Linux وتجد نفسك تُكرر نفس الأوامر يوميًا، فقد حان الوقت لتعلّم Bash Scripting. بدلاً من كتابة 20 أمرًا يدويًا لأخذ نسخة احتياطية أو فحص حالة السيرفر، يمكنك كتابة سكربت واحد يُنفّذ كل شيء بضغطة زر. في هذا الدليل الشامل ستتعلم Bash Scripting من الصفر مع تطبيقات عملية مباشرة على إدارة السيرفرات.

ما هو Bash Scripting ولماذا تحتاجه

Bash Scripting هو كتابة سلسلة من أوامر Linux في ملف نصي واحد يُنفَّذ بالترتيب تلقائيًا. Bash هو اختصار لـ Bourne Again Shell وهو مُفسّر الأوامر الافتراضي في معظم توزيعات Linux. يمكنك الرجوع إلى دليل GNU Bash الرسمي للاطلاع على المرجع الكامل للغة.

لماذا يحتاج مدير السيرفر هذه المهارة؟ إليك الأسباب العملية:

  • توفير الوقت: مهمة تستغرق 15 دقيقة يدويًا يمكن أتمتتها بسكربت يُنفَّذ في ثوانٍ
  • تقليل الأخطاء: السكربت يُنفّذ نفس الخطوات بدقة في كل مرة بدون نسيان خطوة
  • الأتمتة: جدولة السكربتات للتنفيذ تلقائيًا عبر Cron (نسخ احتياطي، فحص أمني، تنظيف)
  • التوثيق: السكربت هو توثيق حي للعمليات – يمكن لأي شخص فهم ما يحدث
  • التكرار: نفس السكربت يعمل على أي سيرفر Linux بدون تعديل كبير

Bash ليس لغة برمجة معقدة. إذا كنت تعرف أوامر Linux الأساسية مثل ls و cd و cp و grep، فأنت جاهز لكتابة سكربتات Bash. كل ما تحتاجه هو تعلّم بعض الهياكل مثل المتغيرات والشروط والحلقات.

كتابة أول سكربت Bash

لنبدأ الرحلة بكتابة أول سكربت بسيط. كل سكربت Bash يبدأ بسطر يُسمّى Shebang يُخبر النظام أن هذا الملف يجب تنفيذه بواسطة Bash:

#!/bin/bash
# هذا أول سكربت Bash - hello.sh
# التعليقات تبدأ بعلامة #

echo "مرحبًا! هذا أول سكربت Bash لي"
echo "التاريخ والوقت الحالي: $(date)"
echo "اسم المستخدم: $(whoami)"
echo "المجلد الحالي: $(pwd)"
echo "نظام التشغيل: $(uname -s)"
echo "اسم السيرفر: $(hostname)"

لتنفيذ السكربت:

# إنشاء الملف
nano hello.sh

# جعل السكربت قابلاً للتنفيذ
chmod +x hello.sh

# تنفيذ السكربت (ثلاث طرق)
./hello.sh          # الطريقة المباشرة
bash hello.sh       # تمرير للـ bash
source hello.sh     # تنفيذ في الجلسة الحالية

لنكتب سكربت أكثر عملية يعرض معلومات السيرفر:

#!/bin/bash
# server-info.sh - عرض معلومات السيرفر الأساسية

echo "========================================"
echo "       معلومات السيرفر"
echo "========================================"
echo ""
echo "اسم السيرفر:     $(hostname)"
echo "نظام التشغيل:    $(cat /etc/os-release | grep PRETTY_NAME | cut -d'"' -f2)"
echo "نواة النظام:     $(uname -r)"
echo "المعالج:         $(nproc) cores"
echo "الذاكرة:         $(free -h | awk '/Mem:/{print $2}') إجمالي | $(free -h | awk '/Mem:/{print $3}') مستخدم"
echo "التخزين:         $(df -h / | awk 'NR==2{print $2}') إجمالي | $(df -h / | awk 'NR==2{print $3}') مستخدم ($(df -h / | awk 'NR==2{print $5}'))"
echo "وقت التشغيل:     $(uptime -p)"
echo "عنوان IP:        $(hostname -I | awk '{print $1}')"
echo ""
echo "========================================"

المتغيرات والعمليات الحسابية

المتغيرات هي أساس أي لغة برمجة، وفي Bash التعامل معها بسيط ومباشر. إليك القواعد الأساسية:

#!/bin/bash
# variables.sh - التعامل مع المتغيرات

# === تعريف المتغيرات ===
# ملاحظة مهمة: لا تضع مسافات حول علامة =
NAME="مرام هوست"
PORT=443
IS_ACTIVE=true

# خطأ شائع (لا تفعل هذا):
# NAME = "مرام هوست"  # سيُعطي خطأ!

# === قراءة المتغيرات ===
echo "اسم الخدمة: $NAME"
echo "المنفذ: $PORT"
echo "نشط: $IS_ACTIVE"

# === متغيرات خاصة ===
echo "اسم السكربت: $0"
echo "أول معامل: $1"
echo "ثاني معامل: $2"
echo "عدد المعاملات: $#"
echo "جميع المعاملات: $@"
echo "كود الخروج السابق: $?"
echo "PID الحالي: $$"

# === استبدال الأوامر ===
CURRENT_DATE=$(date +%Y-%m-%d)
DISK_USAGE=$(df -h / | awk 'NR==2{print $5}')
MEMORY_FREE=$(free -m | awk '/Mem:/{print $4}')

echo "التاريخ: $CURRENT_DATE"
echo "استخدام القرص: $DISK_USAGE"
echo "ذاكرة متاحة: ${MEMORY_FREE}MB"

# === العمليات الحسابية ===
A=10
B=3

echo "الجمع: $((A + B))"        # 13
echo "الطرح: $((A - B))"        # 7
echo "الضرب: $((A * B))"        # 30
echo "القسمة: $((A / B))"       # 3
echo "باقي القسمة: $((A % B))"  # 1

# حسابات أكثر دقة (أعداد عشرية) باستخدام bc
RESULT=$(echo "scale=2; 10 / 3" | bc)
echo "القسمة الدقيقة: $RESULT"  # 3.33

# === المصفوفات ===
SERVERS=("server1" "server2" "server3")
echo "أول سيرفر: ${SERVERS[0]}"
echo "عدد السيرفرات: ${#SERVERS[@]}"
echo "جميع السيرفرات: ${SERVERS[@]}"

الشروط (if/else)

الشروط تُتيح لك اتخاذ قرارات داخل السكربت بناءً على حالة معينة. هذا ضروري جدًا في سكربتات إدارة السيرفرات:

#!/bin/bash
# conditions.sh - الشروط

# === شرط بسيط ===
DISK_USAGE=$(df / | awk 'NR==2{print $5}' | tr -d '%')

if [ "$DISK_USAGE" -gt 80 ]; then
    echo "تحذير: استخدام القرص مرتفع ($DISK_USAGE%)"
elif [ "$DISK_USAGE" -gt 60 ]; then
    echo "تنبيه: استخدام القرص متوسط ($DISK_USAGE%)"
else
    echo "جيد: استخدام القرص طبيعي ($DISK_USAGE%)"
fi

# === مقارنة الأرقام ===
# -eq يساوي | -ne لا يساوي | -gt أكبر من
# -lt أصغر من | -ge أكبر أو يساوي | -le أصغر أو يساوي

# === مقارنة النصوص ===
STATUS="active"
if [ "$STATUS" = "active" ]; then
    echo "الخدمة تعمل"
fi

# !== (لا يساوي)
if [ "$STATUS" != "stopped" ]; then
    echo "الخدمة ليست متوقفة"
fi

# === فحص الملفات والمجلدات ===
# -f ملف موجود | -d مجلد موجود | -r قابل للقراءة
# -w قابل للكتابة | -x قابل للتنفيذ | -s حجمه أكبر من 0

CONFIG_FILE="/etc/nginx/nginx.conf"
if [ -f "$CONFIG_FILE" ]; then
    echo "ملف Nginx موجود"
else
    echo "ملف Nginx غير موجود!"
fi

BACKUP_DIR="/var/backups"
if [ -d "$BACKUP_DIR" ]; then
    echo "مجلد النسخ الاحتياطي موجود"
else
    echo "إنشاء مجلد النسخ الاحتياطي..."
    mkdir -p "$BACKUP_DIR"
fi

# === الشروط المركبة ===
# && (AND) | || (OR)
MEMORY_FREE=$(free -m | awk '/Mem:/{print $4}')
CPU_LOAD=$(uptime | awk -F'load average: ' '{print $2}' | cut -d',' -f1 | tr -d ' ')

if [ "$MEMORY_FREE" -lt 200 ] && [ "$(echo "$CPU_LOAD > 2" | bc)" -eq 1 ]; then
    echo "تحذير: السيرفر تحت ضغط عالٍ!"
fi

# === فحص هل خدمة تعمل ===
SERVICE="nginx"
if systemctl is-active --quiet $SERVICE; then
    echo "$SERVICE يعمل بشكل طبيعي"
else
    echo "$SERVICE متوقف! جارٍ إعادة التشغيل..."
    sudo systemctl start $SERVICE
fi

الحلقات (for/while)

الحلقات تُمكّنك من تكرار عملية معينة على مجموعة من العناصر. مفيدة جدًا عند إدارة عدة سيرفرات أو ملفات:

#!/bin/bash
# loops.sh - الحلقات
# === حلقة for الأساسية ===
echo "=== فحص الخدمات ==="
for SERVICE in nginx mysql php8.3-fpm redis-server; do
    if systemctl is-active --quiet $SERVICE; then
        echo "  ✓ $SERVICE - يعمل"
    else
        echo "  ✗ $SERVICE - متوقف!"
    fi
done
# === حلقة for مع نطاق ===
echo ""
echo "=== إنشاء مجلدات النسخ الاحتياطي ==="
for i in {1..7}; do
    echo "  مجلد اليوم $i"
done
# === حلقة for على ملفات ===
echo ""
echo "=== ملفات الإعدادات في /etc/nginx/conf.d/ ==="
for FILE in /etc/nginx/conf.d/*.conf; do
    if [ -f "$FILE" ]; then
        echo "  $(basename $FILE) - $(wc -l < $FILE) سطر"
    fi
done
# === حلقة while ===
echo ""
echo "=== انتظار بدء خدمة ==="
RETRIES=0
MAX_RETRIES=5
while [ $RETRIES -lt $MAX_RETRIES ]; do
    if curl -s -o /dev/null -w "%{http_code}" http://localhost | grep -q "200"; then
        echo "  الموقع يعمل بعد $RETRIES محاولة"
        break
    fi
    RETRIES=$((RETRIES + 1))
    echo "  المحاولة $RETRIES من $MAX_RETRIES..."
    sleep 2
done
if [ $RETRIES -eq $MAX_RETRIES ]; then
    echo "  فشل الاتصال بعد $MAX_RETRIES محاولات"
fi
# === قراءة ملف سطرًا بسطر ===
echo ""
echo "=== قراءة قائمة السيرفرات ==="
# إنشاء ملف مؤقت للتجربة
echo -e "192.168.1.10n192.168.1.20n192.168.1.30" > /tmp/servers.txt
while IFS= read -r SERVER; do
    echo "  فحص $SERVER..."
    ping -c 1 -W 1 $SERVER > /dev/null 2>&1
    if [ $? -eq 0 ]; then
        echo "    → متصل"
    else
        echo "    → غير متصل"
    fi
done < /tmp/servers.txt

الدوال (Functions)

الدوال تُتيح لك تنظيم الكود وإعادة استخدامه. بدلاً من تكرار نفس الأوامر عدة مرات، اكتبها في دالة واستدعِها عند الحاجة:

#!/bin/bash
# functions.sh - الدوال

# === تعريف دالة بسيطة ===
print_separator() {
    echo "=========================================="
}

# === دالة مع معاملات ===
log_message() {
    local LEVEL=$1    # local تجعل المتغير محلياً داخل الدالة
    local MESSAGE=$2
    local TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
    echo "[$TIMESTAMP] [$LEVEL] $MESSAGE"
}

# === دالة مع قيمة إرجاع ===
check_service() {
    local SERVICE=$1
    if systemctl is-active --quiet $SERVICE 2>/dev/null; then
        return 0  # نجاح
    else
        return 1  # فشل
    fi
}

get_disk_percent() {
    df / | awk 'NR==2{print $5}' | tr -d '%'
}

# === استخدام الدوال ===
print_separator
log_message "INFO" "بدء فحص السيرفر"
print_separator

# فحص خدمات
for SVC in nginx mysql ssh; do
    if check_service $SVC; then
        log_message "OK" "$SVC يعمل"
    else
        log_message "ERROR" "$SVC متوقف!"
    fi
done

# فحص القرص
DISK=$(get_disk_percent)
if [ "$DISK" -gt 80 ]; then
    log_message "WARN" "استخدام القرص مرتفع: ${DISK}%"
else
    log_message "OK" "استخدام القرص طبيعي: ${DISK}%"
fi

print_separator
log_message "INFO" "انتهى الفحص"

5 سكربتات عملية لإدارة السيرفر

الآن بعد أن تعلّمت أساسيات Bash Scripting، دعنا نبني 5 سكربتات عملية يحتاجها كل مدير سيرفر. هذه السكربتات هي التطبيق الحقيقي لمهاراتك في بيئة الإنتاج.

1. سكربت النسخ الاحتياطي الشامل

#!/bin/bash
# backup.sh - نسخ احتياطي شامل للمواقع وقواعد البيانات

set -euo pipefail  # إيقاف السكربت عند أي خطأ

# === الإعدادات ===
BACKUP_ROOT="/var/backups/sites"
WEB_ROOT="/var/www"
DATE=$(date +%Y%m%d_%H%M%S)
KEEP_DAYS=14
LOG_FILE="/var/log/backup.log"
MYSQL_USER="backup_user"
MYSQL_PASS="BackupPass_2026!"

# === دالة التسجيل ===
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

# === دالة النسخ ===
backup_site() {
    local SITE=$1
    local SITE_DIR="$WEB_ROOT/$SITE"
    local BACKUP_DIR="$BACKUP_ROOT/$SITE/$DATE"

    if [ ! -d "$SITE_DIR" ]; then
        log "تحذير: $SITE_DIR غير موجود - تخطي"
        return
    fi

    mkdir -p "$BACKUP_DIR"

    # نسخ الملفات
    log "  → نسخ ملفات $SITE..."
    tar -czf "$BACKUP_DIR/files.tar.gz" -C "$SITE_DIR" . 2>/dev/null
    log "    حجم الملفات: $(du -sh "$BACKUP_DIR/files.tar.gz" | awk '{print $1}')"

    # نسخ قاعدة البيانات (إذا كان wp-config.php موجود)
    if [ -f "$SITE_DIR/wp-config.php" ]; then
        DB_NAME=$(grep "DB_NAME" "$SITE_DIR/wp-config.php" | cut -d"'" -f4)
        if [ -n "$DB_NAME" ]; then
            log "  → نسخ قاعدة بيانات $DB_NAME..."
            mysqldump -u "$MYSQL_USER" -p"$MYSQL_PASS" "$DB_NAME" 2>/dev/null | gzip > "$BACKUP_DIR/database.sql.gz"
            log "    حجم القاعدة: $(du -sh "$BACKUP_DIR/database.sql.gz" | awk '{print $1}')"
        fi
    fi
}

# === بدء النسخ ===
log "========== بدء النسخ الاحتياطي =========="

for SITE_DIR in $WEB_ROOT/*/; do
    SITE=$(basename "$SITE_DIR")
    log "نسخ $SITE..."
    backup_site "$SITE"
done

# حذف النسخ القديمة
log "حذف النسخ الأقدم من $KEEP_DAYS يوم..."
find "$BACKUP_ROOT" -maxdepth 3 -type d -mtime +$KEEP_DAYS -exec rm -rf {} + 2>/dev/null

# عرض ملخص
TOTAL_SIZE=$(du -sh "$BACKUP_ROOT" | awk '{print $1}')
log "الحجم الكلي للنسخ: $TOTAL_SIZE"
log "========== اكتمل النسخ الاحتياطي =========="

2. سكربت مراقبة السيرفر

#!/bin/bash
# monitor.sh - مراقبة حالة السيرفر وإرسال تنبيهات

# === الإعدادات ===
CPU_THRESHOLD=80
RAM_THRESHOLD=85
DISK_THRESHOLD=80
ALERT_EMAIL="[email protected]"

# === جمع المعلومات ===
CPU_USAGE=$(top -bn1 | grep "Cpu(s)" | awk '{print int($2)}')
RAM_USAGE=$(free | awk '/Mem:/{printf "%d", $3/$2 * 100}')
DISK_USAGE=$(df / | awk 'NR==2{print $5}' | tr -d '%')
LOAD_AVG=$(uptime | awk -F'load average: ' '{print $2}')

ALERTS=""

# === فحص الموارد ===
if [ "$CPU_USAGE" -gt "$CPU_THRESHOLD" ]; then
    ALERTS+="[CPU] استخدام المعالج: ${CPU_USAGE}% (الحد: ${CPU_THRESHOLD}%)n"
fi

if [ "$RAM_USAGE" -gt "$RAM_THRESHOLD" ]; then
    ALERTS+="[RAM] استخدام الذاكرة: ${RAM_USAGE}% (الحد: ${RAM_THRESHOLD}%)n"
fi

if [ "$DISK_USAGE" -gt "$DISK_THRESHOLD" ]; then
    ALERTS+="[DISK] استخدام القرص: ${DISK_USAGE}% (الحد: ${DISK_THRESHOLD}%)n"
fi

# === فحص الخدمات ===
for SERVICE in nginx mysql php8.3-fpm; do
    if ! systemctl is-active --quiet $SERVICE 2>/dev/null; then
        ALERTS+="[SERVICE] $SERVICE متوقف!n"
        # محاولة إعادة التشغيل
        sudo systemctl start $SERVICE 2>/dev/null
        if systemctl is-active --quiet $SERVICE; then
            ALERTS+="[SERVICE] تم إعادة تشغيل $SERVICE بنجاحn"
        fi
    fi
done

# === عرض التقرير ===
echo "=== تقرير السيرفر - $(date) ==="
echo "CPU: ${CPU_USAGE}% | RAM: ${RAM_USAGE}% | DISK: ${DISK_USAGE}%"
echo "Load: $LOAD_AVG"

if [ -n "$ALERTS" ]; then
    echo ""
    echo "⚠ تنبيهات:"
    echo -e "$ALERTS"
    # إرسال بريد تنبيه (يتطلب إعداد mailutils)
    # echo -e "$ALERTS" | mail -s "تنبيه السيرفر $(hostname)" $ALERT_EMAIL
else
    echo "✓ كل شيء يعمل بشكل طبيعي"
fi

3. سكربت تنظيف السيرفر

#!/bin/bash
# cleanup.sh - تنظيف السيرفر من الملفات المؤقتة والسجلات القديمة

log() { echo "[$(date '+%H:%M:%S')] $1"; }

log "=== بدء التنظيف ==="
FREED=0

# تنظيف apt cache
BEFORE=$(du -sm /var/cache/apt | awk '{print $1}')
sudo apt-get clean -y > /dev/null 2>&1
sudo apt-get autoremove -y > /dev/null 2>&1
AFTER=$(du -sm /var/cache/apt | awk '{print $1}')
SAVED=$((BEFORE - AFTER))
FREED=$((FREED + SAVED))
log "APT cache: تم تحرير ${SAVED}MB"

# تنظيف السجلات القديمة
BEFORE=$(du -sm /var/log | awk '{print $1}')
sudo find /var/log -name "*.gz" -mtime +7 -delete 2>/dev/null
sudo find /var/log -name "*.old" -delete 2>/dev/null
sudo journalctl --vacuum-time=7d > /dev/null 2>&1
AFTER=$(du -sm /var/log | awk '{print $1}')
SAVED=$((BEFORE - AFTER))
FREED=$((FREED + SAVED))
log "السجلات: تم تحرير ${SAVED}MB"

# تنظيف /tmp
BEFORE=$(du -sm /tmp | awk '{print $1}')
sudo find /tmp -type f -atime +3 -delete 2>/dev/null
AFTER=$(du -sm /tmp | awk '{print $1}')
SAVED=$((BEFORE - AFTER))
FREED=$((FREED + SAVED))
log "ملفات مؤقتة: تم تحرير ${SAVED}MB"

# تنظيف Docker (إن كان مُثبّتاً)
if command -v docker &> /dev/null; then
    BEFORE=$(docker system df --format '{{.Size}}' 2>/dev/null | head -1)
    docker system prune -f > /dev/null 2>&1
    log "Docker: تم التنظيف (الحجم السابق: $BEFORE)"
fi

log "=== اكتمل التنظيف - تم تحرير ~${FREED}MB ==="

4. سكربت التقارير اليومية

#!/bin/bash
# daily-report.sh - تقرير يومي شامل عن حالة السيرفر

REPORT="/tmp/daily_report_$(date +%Y%m%d).txt"

{
echo "========================================================"
echo "  التقرير اليومي للسيرفر: $(hostname)"
echo "  التاريخ: $(date '+%Y-%m-%d %H:%M')"
echo "========================================================"

echo ""
echo "--- الموارد ---"
echo "وقت التشغيل: $(uptime -p)"
echo "المعالج:      $(top -bn1 | grep 'Cpu' | awk '{print $2}')% مستخدم"
echo "الذاكرة:      $(free -h | awk '/Mem:/{print $3"/"$2}')"
echo "Swap:         $(free -h | awk '/Swap:/{print $3"/"$2}')"
echo "القرص (/):    $(df -h / | awk 'NR==2{print $3"/"$2" ("$5")"}')"

echo ""
echo "--- أكثر 5 عمليات استهلاكاً للذاكرة ---"
ps aux --sort=-%mem | head -6 | awk '{printf "  %-15s %s%%n", $11, $4}'

echo ""
echo "--- حالة الخدمات ---"
for SVC in nginx mysql php8.3-fpm redis-server fail2ban; do
    STATUS=$(systemctl is-active $SVC 2>/dev/null || echo "غير مُثبّت")
    printf "  %-20s %sn" "$SVC" "$STATUS"
done

echo ""
echo "--- آخر 5 تسجيلات دخول ---"
last -5 -w | head -5

echo ""
echo "--- محاولات SSH الفاشلة (آخر 24 ساعة) ---"
FAILED=$(grep "Failed password" /var/log/auth.log 2>/dev/null | grep "$(date +%b %d)" | wc -l)
echo "  عدد المحاولات الفاشلة: $FAILED"

echo ""
echo "========================================================"
} > "$REPORT"

cat "$REPORT"

5. سكربت الفحص الأمني

#!/bin/bash
# security-audit.sh - فحص أمني سريع للسيرفر

echo "=== الفحص الأمني - $(date) ==="
ISSUES=0

# فحص المستخدمين بصلاحيات root
ROOT_USERS=$(awk -F: '$3 == 0 {print $1}' /etc/passwd)
if [ $(echo "$ROOT_USERS" | wc -w) -gt 1 ]; then
    echo "[!] مستخدمون بصلاحيات root: $ROOT_USERS"
    ISSUES=$((ISSUES + 1))
fi

# فحص SSH
if grep -q "PermitRootLogin yes" /etc/ssh/sshd_config 2>/dev/null; then
    echo "[!] تسجيل دخول root عبر SSH مُفعّل"
    ISSUES=$((ISSUES + 1))
fi
if grep -q "PasswordAuthentication yes" /etc/ssh/sshd_config 2>/dev/null; then
    echo "[!] تسجيل الدخول بكلمة مرور عبر SSH مُفعّل"
    ISSUES=$((ISSUES + 1))
fi

# فحص الجدار الناري
if ! sudo ufw status | grep -q "Status: active" 2>/dev/null; then
    echo "[!] الجدار الناري UFW غير مُفعّل"
    ISSUES=$((ISSUES + 1))
fi

# فحص تحديثات الأمان
UPDATES=$(sudo apt list --upgradable 2>/dev/null | grep -c security)
if [ "$UPDATES" -gt 0 ]; then
    echo "[!] $UPDATES تحديث أمني متوفر"
    ISSUES=$((ISSUES + 1))
fi

# فحص ملفات SUID المشبوهة
SUID_COUNT=$(find / -perm -4000 -type f 2>/dev/null | wc -l)
echo "[i] ملفات SUID: $SUID_COUNT"

# فحص المنافذ المفتوحة
echo ""
echo "المنافذ المفتوحة:"
sudo ss -tlnp | grep LISTEN | awk '{print "  "$4" → "$7}' | sed 's/users:(("/  /;s/".*//;s/,fd.*//g'

echo ""
if [ $ISSUES -eq 0 ]; then
    echo "✓ لم يتم اكتشاف مشاكل أمنية"
else
    echo "⚠ تم اكتشاف $ISSUES مشكلة أمنية تحتاج مراجعة"
fi

التعامل مع الملفات والمجلدات

Bash يتفوّق في التعامل مع الملفات والمجلدات. يمكنك الرجوع إلى مشروع توثيق Linux (TLDP) للمزيد من الأمثلة المتقدمة. إليك العمليات الأكثر استخدامًا:

#!/bin/bash
# file-operations.sh - التعامل مع الملفات
# === قراءة ملف ===
while IFS= read -r line; do
    echo "السطر: $line"
done < /etc/hostname
# === كتابة في ملف ===
echo "محتوى جديد" > file.txt      # كتابة (حذف المحتوى السابق)
echo "سطر إضافي" >> file.txt      # إلحاق (إضافة للمحتوى)
# === البحث في الملفات ===
# البحث عن نص في ملفات PHP
grep -rl "eval(" /var/www/ --include="*.php" 2>/dev/null
# البحث عن ملفات كبيرة (أكبر من 100MB)
find / -type f -size +100M 2>/dev/null | head -10
# البحث عن ملفات عُدّلت في آخر 24 ساعة
find /var/www -type f -mtime -1 -name "*.php" 2>/dev/null
# === معالجة النصوص ===
# استخراج عناوين IP من سجل الوصول
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10
# استخراج معلومات محددة باستخدام sed
cat /etc/os-release | sed -n 's/^PRETTY_NAME="(.*)"/1/p'
# === إعادة تسمية ملفات دفعة واحدة ===
# تغيير امتداد جميع ملفات .jpeg إلى .jpg
for FILE in /path/to/images/*.jpeg; do
    [ -f "$FILE" ] && mv "$FILE" "${FILE%.jpeg}.jpg"
done
# === حساب checksums ===
md5sum /var/www/site/wp-config.php
sha256sum /var/www/site/wp-includes/version.php

جدولة السكربتات مع Cron

القوة الحقيقية لـ Bash Scripting تظهر عند جدولة السكربتات للتنفيذ التلقائي باستخدام Cron. هذا يُحوّل السكربتات من أدوات يدوية إلى نظام أتمتة كامل:

# صيغة Cron:
# دقيقة  ساعة  يوم_الشهر  الشهر  يوم_الأسبوع  الأمر
#  0-59   0-23    1-31      1-12     0-7

# فتح محرر crontab
crontab -e

# === أمثلة عملية ===

# نسخ احتياطي يومياً الساعة 3:00 فجراً
0 3 * * * /usr/local/bin/backup.sh >> /var/log/backup-cron.log 2>&1

# مراقبة كل 5 دقائق
*/5 * * * * /usr/local/bin/monitor.sh >> /var/log/monitor.log 2>&1

# تنظيف أسبوعياً يوم الأحد الساعة 4:00 فجراً
0 4 * * 0 /usr/local/bin/cleanup.sh >> /var/log/cleanup.log 2>&1

# تقرير يومي الساعة 8:00 صباحاً
0 8 * * * /usr/local/bin/daily-report.sh | mail -s "تقرير $(hostname)" [email protected]

# فحص أمني أسبوعياً
0 2 * * 1 /usr/local/bin/security-audit.sh >> /var/log/security.log 2>&1

# تجديد شهادة SSL شهرياً
0 3 1 * * certbot renew --quiet --post-hook "systemctl reload nginx"

# === عرض الجدولة الحالية ===
crontab -l

# === تشغيل يدوي للاختبار ===
# تأكد دائماً أن السكربت يعمل يدوياً قبل جدولته
bash -x /usr/local/bin/backup.sh  # وضع التتبع (debug)

نصيحة مهمة: دائمًا أضف تحويل الأخطاء (2>&1) وسجّل المخرجات في ملف log عند استخدام Cron، لأن Cron لا يعرض المخرجات على الشاشة.

أخطاء شائعة في Bash

حتى مع الخبرة، هناك أخطاء شائعة يقع فيها الكثيرون. تعرّف عليها لتتجنّبها. يمكنك استخدام أداة ShellCheck لفحص سكربتاتك تلقائيًا:

#!/bin/bash
# common-mistakes.sh - أخطاء شائعة

# === خطأ 1: مسافات حول = ===
# ✗ خطأ:
# NAME = "value"
# ✓ صحيح:
NAME="value"

# === خطأ 2: عدم تضمين المتغيرات بعلامات اقتباس ===
# ✗ خطأ (يفشل إذا احتوى الاسم على مسافات):
# if [ -f $FILENAME ]; then
# ✓ صحيح:
FILENAME="/path/to/my file.txt"
if [ -f "$FILENAME" ]; then
    echo "الملف موجود"
fi

# === خطأ 3: استخدام == بدلاً من = في [ ] ===
# في [ ] استخدم = للمقارنة
# في [[ ]] يمكنك استخدام == أيضاً
if [ "$NAME" = "value" ]; then
    echo "متساويان"
fi

# === خطأ 4: نسيان ;; في case ===
OPTION="start"
case $OPTION in
    start)
        echo "بدء"
        ;;    # لا تنسَ ;;
    stop)
        echo "إيقاف"
        ;;
    *)
        echo "خيار غير معروف"
        ;;
esac

# === خطأ 5: عدم التعامل مع الأخطاء ===
# ✗ خطأ - تجاهل الأخطاء:
# cd /nonexistent/path
# rm -rf *   # خطير جداً!

# ✓ صحيح - التحقق أولاً:
TARGET_DIR="/var/www/backup"
if [ -d "$TARGET_DIR" ]; then
    cd "$TARGET_DIR" && rm -rf old_files/
else
    echo "المجلد غير موجود: $TARGET_DIR"
    exit 1
fi

# === خطأ 6: عدم استخدام set -e ===
# أضف هذا في بداية السكربت لإيقافه عند أي خطأ:
# set -euo pipefail
# -e: إيقاف عند أي خطأ
# -u: إيقاف عند استخدام متغير غير معرّف
# -o pipefail: إيقاف عند فشل أي أمر في pipe

نصائح لكتابة سكربتات احترافية

لرفع مستواك من المبتدئ إلى المحترف، اتبع هذه النصائح العملية:

  • ابدأ دائمًا بـ set -euo pipefail: هذا يحمي سكربتك من الاستمرار بعد حدوث خطأ ويمنع الكوارث
  • استخدم متغيرات للمسارات والإعدادات: ضع جميع القيم القابلة للتغيير في متغيرات في أعلى السكربت لسهولة التعديل
  • أضف تعليقات واضحة: كل قسم وكل دالة تحتاج تعليقًا يشرح الغرض منها
  • تحقق من المتطلبات: في بداية السكربت تحقق من وجود الأدوات والصلاحيات المطلوبة
  • أضف رسائل مفيدة: استخدم echo أو دالة log لإعلام المستخدم بما يحدث
  • تعامل مع الأخطاء بذكاء: استخدم trap لتنظيف الملفات المؤقتة عند الفشل
  • استخدم ShellCheck: أداة مجانية تفحص سكربتاتك وتكتشف الأخطاء المحتملة
  • اختبر في بيئة آمنة: لا تُجرّب سكربتات جديدة مباشرة على سيرفر الإنتاج
#!/bin/bash
# template.sh - قالب سكربت احترافي

set -euo pipefail

# === الإعدادات ===
readonly SCRIPT_NAME=$(basename "$0")
readonly SCRIPT_DIR=$(dirname "$(readlink -f "$0")")
readonly LOG_FILE="/var/log/${SCRIPT_NAME%.sh}.log"

# === دوال مساعدة ===
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"; }
error() { log "ERROR: $*" >&2; }
die() { error "$*"; exit 1; }

# === تنظيف عند الخروج ===
cleanup() {
    log "تنظيف الملفات المؤقتة..."
    rm -f /tmp/${SCRIPT_NAME}_* 2>/dev/null
}
trap cleanup EXIT

# === التحقق من المتطلبات ===
check_requirements() {
    # التحقق من صلاحيات root
    if [ "$(id -u)" -ne 0 ]; then
        die "هذا السكربت يحتاج صلاحيات root"
    fi

    # التحقق من وجود الأدوات المطلوبة
    for CMD in curl jq gzip; do
        if ! command -v $CMD &> /dev/null; then
            die "الأداة $CMD غير مُثبّتة"
        fi
    done
}

# === الدالة الرئيسية ===
main() {
    log "=== بدء $SCRIPT_NAME ==="
    check_requirements

    # كودك هنا...

    log "=== اكتمل $SCRIPT_NAME ==="
}

# تشغيل
main "$@"

هذا القالب يتضمن أفضل الممارسات: التعامل مع الأخطاء، التسجيل، التنظيف التلقائي، والتحقق من المتطلبات. انسخه واستخدمه كنقطة بداية لأي سكربت جديد.

الخلاصة

Bash Scripting هو من أهم المهارات التي يحتاجها كل مدير سيرفر ومطوّر يعمل مع Linux. في هذا الدليل تعلّمت الأساسيات من المتغيرات والشروط والحلقات والدوال، ثم انتقلنا إلى التطبيقات العملية مع 5 سكربتات جاهزة للاستخدام في بيئة الإنتاج.

المفتاح لإتقان Bash Scripting هو الممارسة المستمرة. ابدأ بأتمتة المهام البسيطة التي تُكررها يوميًا، ثم تدرّج نحو السكربتات الأكثر تعقيدًا. مع الوقت ستبني مكتبة من السكربتات التي توفّر عليك ساعات من العمل اليدوي كل أسبوع. تذكّر أن كل سيرفر VPS من مرام هوست يأتي بصلاحيات root كاملة تُتيح لك تشغيل سكربتاتك بحرية تامة.

سيرفرات Linux من مرام هوست مع صلاحيات root كاملة

احصل على سيرفر VPS بنظام Linux مع صلاحيات root كاملة لتشغيل سكربتات Bash Scripting وأتمتة جميع مهام إدارة السيرفر. أداء عالٍ ودعم فني على مدار الساعة. ابدأ من هنا.

الأسئلة الشائعة

هل أحتاج خبرة برمجية مسبقة لتعلّم Bash Scripting؟

لا، لا يتطلب خبرة برمجية مسبقة. إذا كنت تعرف أوامر Linux الأساسية (ls, cd, cp, mv, grep)، فأنت جاهز للبدء. Bash Scripting أقرب إلى كتابة سلسلة من الأوامر منه إلى البرمجة التقليدية. ابدأ بسكربتات بسيطة وتدرّج في التعقيد.

ما الفرق بين Bash و Shell Scripting؟

Shell هو المصطلح العام لأي مُفسّر أوامر في Linux (مثل sh, bash, zsh, fish). Bash هو أشهر وأكثر أنواع Shell استخدامًا وهو الافتراضي في معظم توزيعات Linux. عندما يقول شخص Bash Scripting فهو يقصد كتابة سكربتات تعمل تحديدًا مع مُفسّر Bash الذي يدعم ميزات متقدمة أكثر من sh الأساسي.

هل يمكنني استخدام Bash Scripting على Windows؟

نعم، يمكنك استخدام Bash على Windows من خلال WSL (Windows Subsystem for Linux) الذي يوفّر بيئة Linux كاملة داخل Windows. أيضًا أدوات مثل Git Bash توفّر بيئة محدودة على Windows. لكن الاستخدام الأمثل لـ Bash Scripting يكون على سيرفرات Linux حيث يمكنك الاستفادة من جميع الأدوات والأوامر المتاحة.

كيف أتعلّم Bash Scripting بشكل أسرع؟

أفضل طريقة للتعلّم هي الممارسة العملية. ابدأ بتحويل المهام اليدوية التي تقوم بها يوميًا إلى سكربتات. اقرأ سكربتات الآخرين على GitHub لتتعلم أنماطًا جديدة. استخدم ShellCheck لفحص أكوادك. وأخيرًا، اقرأ دليل GNU Bash الرسمي كمرجع عندما تحتاج فهم ميزة معينة بالتفصيل.