اتوماسیون یعنی تبدیل کارهای تکراری و زمانبر به اسکریپتهایی که بدون نیاز به دخالت دستی، بهطور منظم یا شرطی اجرا میشوند. در این درس، سناریوهای واقعی و پرکاربرد اتوماسیون در سیستمهای لینوکسی را بررسی میکنیم: از پشتیبانگیری و پردازش فایل گرفته تا نگهداری سیستم و برنامهریزی با cron. هدف این است که پس از مطالعه، بتوانید هر کار تکراری را به یک اسکریپت خودکار تبدیل کنید.
اسکریپتهای پشتیبانگیری (Backup Scripts)
پشتیبانگیری از دایرکتوریهای مهم، یکی از رایجترین کاربردهای اسکریپتنویسی است. یک اسکریپت خوب باید تاریخ را در نام فایل درج کند، حجم را کاهش دهد (فشردهسازی)، و گزارش موفقیت یا شکست را ثبت نماید.
پشتیبانگیری ساده با tar و تاریخ
#!/bin/bash set -euo pipefail # تعریف متغیرها SOURCE_DIR="/home/user/documents" BACKUP_DIR="/backups" DATE=$(date +%Y%m%d_%H%M%S) BACKUP_FILE="$BACKUP_DIR/backup_$DATE.tar.gz" echo "شروع پشتیبانگیری از $SOURCE_DIR به $BACKUP_FILE" # ایجاد دایرکتوری مقصد در صورت وجود نداشتن mkdir -p "$BACKUP_DIR" # فشردهسازی و بستهبندی tar -czf "$BACKUP_FILE" -C "$(dirname "$SOURCE_DIR")" "$(basename "$SOURCE_DIR")" # بررسی موفقیت if [[ $? -eq 0 ]]; then echo "$(date) - پشتیبانگیری موفق: $BACKUP_FILE" >> "$BACKUP_DIR/backup.log" else echo "$(date) - خطا در پشتیبانگیری!" >> "$BACKUP_DIR/backup.log" exit 1 fi # حذف پشتیبانهای قدیمیتر از ۳۰ روز find "$BACKUP_DIR" -name "backup_*.tar.gz" -type f -mtime +30 -delete echo "پشتیبانگیری کامل شد."
توضیح اجزای کلیدی:
| جزء دستور | توضیح |
|---|---|
tar -czf | c برای create، z برای فشردهسازی با gzip، f برای تعیین نام فایل |
-C "$(dirname ...)" | تغییر دایرکتوری قبل از بستهبندی تا مسیر کامل درون فایل ذخیره نشود |
date +%Y%m%d_%H%M%S | تولید زمانمهر (timestamp) با دقت ثانیه برای نامگذاری یکتا |
find ... -mtime +30 -delete | حذف فایلهایی که بیش از ۳۰ روز از آخرین تغییرشان گذشته است |
پشتیبانگیری افزایشی با rsync
برای دایرکتوریهای بسیار بزرگ که تغییرات جزئی دارند، rsync بسیار کارآمدتر از tar است، زیرا فقط تفاوت (فایلهای تغییر دادهشده) را کپی میکند.
#!/bin/bash set -euo pipefail SOURCE="/var/www/html" DEST="/backups/www_sync" LOG="/var/log/backup_rsync.log" # rsync با آرشیو (حفظ مجوزها) و حذف فایلهای پاکشده در مبدأ rsync -avz --delete --exclude="cache/" "$SOURCE/" "$DEST/" >> "$LOG" 2>&1 # کد خروج rsync را بررسی کن (24 به معنی تغییرات جزئی است که قابل چشمپوشی است) if [[ $? -eq 0 || $? -eq 24 ]]; then echo "$(date) - همگامسازی rsync موفق" >> "$LOG" else echo "$(date) - خطا در همگامسازی rsync" >> "$LOG" exit 1 fi
rsync -avzیعنی حالت آرشیو (-aکه شامل-rlptgoDمیشود)، خروجی verbose (-v)، و فشردهسازی در حین انتقال (-z). گزینهی--deleteباعث میشود فایلهایی که در مبدأ حذف شدهاند، در مقصد نیز حذف شوند تا پشتیبان دقیقاً آینه باشد.
پردازش فایلها (File Processing)
اتوماسیون پردازش فایل، شامل تغییر نام دستهجمعی، جایگزینی متن درون فایلها، یا استخراج داده از لاگها و CSVهاست.
تغییر نام دستهجمعی با حلقه و mv
فرض کنید میخواهید تمام فایلهای .jpg یک دایرکتوری را به فرمت image_001.jpg، image_002.jpg و ... تغییر نام دهید:
#!/bin/bash set -euo pipefail counter=1 for file in *.jpg; do # اگر فایلی با الگو تطابق نداشت، حلقه را بشکن [[ -e "$file" ]] || break # تولید نام جدید با padding 3 رقمی new_name=$(printf "image_%03d.jpg" "$counter") echo "تغییر نام: $file -> $new_name" mv "$file" "$new_name" ((counter++)) done
جایگزینی متن در همهی فایلهای یک دایرکتوری (sed -i)
اگر نیاز دارید یک کلمه یا تنظیمات را در چندین فایل متنی تغییر دهید:
#!/bin/bash set -euo pipefail SEARCH="old_domain.com" REPLACE="new_domain.com" # جایگزینی در تمام فایلهای .conf (ایجاد بکآپ با پسوند .bak) find /etc/nginx/ -name "*.conf" -type f -exec sed -i.bak "s/$SEARCH/$REPLACE/g" {} \;
sed -i.bakیعنی جایگزینی را در خود فایل (in-place) انجام بده، اما قبلاً یک فایل بکآپ با پسوند.bakایجاد کن. استفاده از{} \;درfindبه این معناست که دستورsedبرای هر فایل بهصورت جداگانه اجرا میشود. اگر از{} +استفاده کنید،findتا حد ممکن فایلها را بهصورت گروهی بهsedمیدهد.
پردازش CSV و استخراج دادهها
#!/bin/bash set -euo pipefail INPUT_FILE="users.csv" OUTPUT_FILE="active_users.txt" # حذف هدر و استخراج ستون دوم (نام کاربری) که در ستون سوم مقدار "active" دارد while IFS=, read -r id name status; do # پرش از خط هدر if [[ "$id" == "id" ]]; then continue; fi if [[ "$status" == "active" ]]; then echo "$name" >> "$OUTPUT_FILE" fi done < "$INPUT_FILE" echo "لیست کاربران فعال در $OUTPUT_FILE ذخیره شد."
در اینجا
IFS=,باعث میشودreadبر اساس ویرگول جدا کند. دقت کنید که این روش برای CSVهای ساده کار میکند؛ اگر فیلدها شامل ویرگول یا خط جدید باشند، بهتر است از ابزارهای تخصصی مثلcsvtoolیاawkاستفاده کنید.
نگهداری سیستم (System Maintenance)
اسکریپتهای نگهداری برای پاکسازی فضای دیسک، چرخش لاگها، و نظارت بر سلامت سیستم ضروری هستند.
پاکسازی خودکار لاگهای قدیمی
#!/bin/bash set -euo pipefail LOG_DIR="/var/log/myapp" DAYS_TO_KEEP=7 echo "پاکسازی لاگهای قدیمیتر از $DAYS_TO_KEEP روز در $LOG_DIR" find "$LOG_DIR" -name "*.log" -type f -mtime "+$DAYS_TO_KEEP" -exec rm -f {} \; # همچنین میتوانیم فایلهای خالی را پاک کنیم find "$LOG_DIR" -name "*.log" -type f -size 0 -delete echo "پاکسازی انجام شد."
نظارت بر فضای دیسک و ارسال هشدار (با df و awk)
#!/bin/bash set -euo pipefail THRESHOLD=80 MOUNT_POINT="/" EMAIL="admin@example.com" # استخراج درصد استفاده بهصورت عددی USAGE=$(df -h "$MOUNT_POINT" | awk 'NR==2 {print $5}' | sed 's/%//') if [[ $USAGE -gt $THRESHOLD ]]; then SUBJECT="هشدار: فضای دیسک $MOUNT_POINT پر شده است" MESSAGE="استفاده از دیسک: ${USAGE}% (آستانه: ${THRESHOLD}%)" echo "$MESSAGE" | mail -s "$SUBJECT" "$EMAIL" # ثبت در لاگ محلی نیز echo "$(date) - $MESSAGE" >> /var/log/disk_alert.log fi
چرخش لاگ (Log Rotation) با logrotate یا دستی
بسیاری از سیستمها از logrotate برای چرخش لاگها استفاده میکنند. اما میتوان یک نسخهی ساده نیز با Bash پیادهسازی کرد:
#!/bin/bash set -euo pipefail LOG_FILE="/var/log/app.log" MAX_SIZE_MB=100 BACKUP_COUNT=5 # بررسی حجم فایل (در بایت) FILE_SIZE=$(stat -c %s "$LOG_FILE") MAX_SIZE_BYTES=$((MAX_SIZE_MB * 1024 * 1024)) if [[ $FILE_SIZE -gt $MAX_SIZE_BYTES ]]; then # چرخش فایلها: log.4 -> log.5, log.3 -> log.4, ... for ((i=BACKUP_COUNT; i>1; i--)); do if [[ -f "${LOG_FILE}.$((i-1))" ]]; then mv "${LOG_FILE}.$((i-1))" "${LOG_FILE}.$i" fi done mv "$LOG_FILE" "${LOG_FILE}.1" # ایجاد فایل لاگ جدید با مجوزهای مناسب touch "$LOG_FILE" echo "$(date) - لاگ چرخش انجام شد." >> "$LOG_FILE" fi
اسکریپتهای اتوماسیون عمومی (افزایش انعطافپذیری)
برای اینکه اسکریپتهای خود را قابل استفادهتر کنید، آنها را پارامترپذیر بسازید و اعتبارسنجی ورودی اضافه کنید.
الگوی اسکریپت با آرگومانهای ورودی
#!/bin/bash set -euo pipefail show_usage() { echo "استفاده: $0 [--source SOURCE_DIR] [--dest DEST_DIR] [--verbose]" echo " --source دایرکتوری مبدأ برای پشتیبانگیری" echo " --dest دایرکتوری مقصد" echo " --verbose نمایش جزئیات بیشتر" exit 1 } SOURCE="" DEST="" VERBOSE=0 while [[ $# -gt 0 ]]; do case $1 in --source) SOURCE="$2" shift 2 ;; --dest) DEST="$2" shift 2 ;; --verbose) VERBOSE=1 shift ;; -h|--help) show_usage ;; *) echo "خطا: آرگومان ناشناخته $1" >&2 show_usage ;; esac done # اعتبارسنجی if [[ -z "$SOURCE" || -z "$DEST" ]]; then echo "خطا: --source و --dest اجباری هستند." >&2 show_usage fi if [[ ! -d "$SOURCE" ]]; then echo "خطا: دایرکتوری $SOURCE وجود ندارد." >&2 exit 1 fi # ادامهی منطق اسکریپت... [[ $VERBOSE -eq 1 ]] && echo "شروع پشتیبانگیری از $SOURCE به $DEST"
برنامهریزی با Cron Jobs
ابزار cron در پسزمینه اجرا میشود و دستورات یا اسکریپتها را در زمانهای از پیش تعیینشده اجرا میکند. این قلب تپندهی اتوماسیون در سیستمهای لینوکسی است.
سینتکس crontab
هر خط در crontab دارای شش فیلد است (دقیقاً به این ترتیب):
| فیلد | محدوده | توضیح |
|---|---|---|
| دقیقه (Minute) | ۰–۵۹ | دقیقهای که دستور اجرا شود |
| ساعت (Hour) | ۰–۲۳ | ساعت روز |
| روز ماه (Day of Month) | ۱–۳۱ | روز ماه |
| ماه (Month) | ۱–۱۲ یا jan-dec | ماه سال |
| روز هفته (Day of Week) | ۰–۷ یا sun-sat (۰ و ۷ هر دو یکشنبه) | روز هفته |
| دستور (Command) | هر دستور معتبر Bash | دستوری که باید اجرا شود |
ویرایش کرونتابل کاربر جاری:
crontab -e
مثالهای پرکاربرد:
| هدف | عبارت کرون |
|---|---|
| اجرای اسکریپت پشتیبان هر روز ساعت ۲ بامداد | 0 2 * * * /opt/scripts/backup.sh |
| پاکسازی لاگها هر یکشنبه ساعت ۳:۳۰ | 30 3 * * 0 /opt/scripts/cleanup.sh |
| اجرای یک اسکریپت هر ۱۵ دقیقه | */15 * * * * /opt/scripts/monitor.sh |
| اجرا در روز اول هر ماه | 0 0 1 * * /opt/scripts/monthly_report.sh |
| اجرا در روزهای کاری (دوشنبه تا جمعه) ساعت ۹ صبح | 0 9 * * 1-5 /opt/scripts/start_day.sh |
*/15یعنی «هر ۱۵ دقیقه». برای «هر ساعت» از0 * * * *استفاده کنید.
تلههای بزرگ در Cron و نحوهی رفع آنها
۱. مشکل PATH:cron با یک محیط بسیار محدود اجرا میشود و PATH آن فقط شامل /usr/bin:/bin است. اگر دستور شما در /usr/local/bin نصب است، پیدا نمیشود.
راهحلها:
-
در بالای
crontab،PATHرا تنظیم کنید:PATH=/usr/local/bin:/usr/bin:/bin:/opt/bin 0 2 * * * my_command
-
در خود اسکریپت، از مسیر مطلق استفاده کنید:
/usr/bin/rsync -av /src/ /dest/ -
در ابتدای اسکریپت،
PATHرا تنظیم کنید:export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
۲. خروجیهای گمشده:
اگر دستور شما خروجی تولید کند و آن را به جایی نبرید، cron آن را بهصورت ایمیل به کاربر ارسال میکند (که ممکن است هرگز دیده نشود).
راهحل: همیشه خروجی را به یک فایل لاگ هدایت کنید:
0 2 * * * /opt/scripts/backup.sh >> /var/log/backup.log 2>&1
۳. متغیرهای محیطی گمشده ($HOME, $USER):
اگر اسکریپت شما از متغیرهای محیطی استفاده میکند، آنها را در کرون تعریف کنید یا در ابتدای اسکریپت مقداردهی نمایید.
۴. دسترسی به فایل:
اسکریپت اجرا شده توسط cron ممکن است مجوز اجرا یا نوشتن در دایرکتوریهای خاص را نداشته باشد. از chmod +x script.sh اطمینان حاصل کنید و دایرکتوریها را با مجوز مناسب ایجاد نمایید.
مثال جامع crontab با متغیرها و لاگدهی
# متغیرهای محیطی سفارشی SHELL=/bin/bash PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin MAILTO=admin@example.com # پشتیبانگیری روزانه از دیتابیس در ساعت ۱ بامداد 0 1 * * * /root/scripts/backup_db.sh >> /var/log/db_backup.log 2>&1 # همگامسازی فایلها با سرور راهدور هر ۶ ساعت 0 */6 * * * rsync -avz /data/ user@remote:/backup/ # پاکسازی کش هر روز در ساعت ۴ 0 4 * * * find /tmp -type f -atime +1 -delete
اشتباهات رایج در اسکریپتهای اتوماسیون
| اشتباه | پیامد | راهحل |
|---|---|---|
استفاده از ~ در کرون | ~ به خانه کاربر جاری گسترش نمییابد | از مسیر مطلق استفاده کنید: /home/user/ |
فراموشی 2>&1 در کرون | خطاها دیده نمیشوند و ایمیلهای بیمخاطب تولید میشوند | همیشه خروجی و خطا را به لاگ بفرستید |
فرض اینکه pwd دایرکتوری اسکریپت است | cron معمولاً از $HOME اجرا میشود | در ابتدای اسکریپت از cd "$(dirname "$0")" استفاده کنید یا از cd به دایرکتوری مشخص |
تغییر IFS بهطور سراسری | پردازش سایر بخشهای اسکریپت خراب میشود | IFS را فقط برای همان دستور تنظیم کنید یا حتماً پس از کار، به حالت اول برگردانید (OLD_IFS="$IFS" و سپس IFS="$OLD_IFS") |
| نادیدهگرفتن کد خروج دستورات | اسکریپت با وجود خطا ادامه مییابد و وضعیت غیرمنتظره ایجاد میکند | از set -e استفاده کنید یا کد خروج را با $? بررسی کنید |
جمعبندی و الگوی نهایی اتوماسیون
یک اسکریپت اتوماسیون ایدهآل باید این ویژگیها را داشته باشد:
-
ایمن: با
set -euo pipefailشروع شود. -
لاگگذار: تمام اقدامات مهم و خطاها را در یک فایل ثبت کند.
-
قابل برنامهریزی: پارامترهای کلیدی (مسیرها، آستانهها) از طریق متغیرهای قابل تغییر تعریف شوند.
-
بازگشتپذیر: قبل از تغییرات مخرب (مثل
rm -rfیاmv)، از دادهها بکآپ بگیرد یا حالت خشک (dry-run) داشته باشد. -
اطلاعرسان: در صورت بروز خطای بحرانی، به اپراتور ایمیل یا پیام هشدار بفرستد.
الگوی نهایی برای یک اسکریپت اتوماسیون:
#!/bin/bash set -Eeuo pipefail # ---------- تنظیمات و متغیرها ---------- LOG_FILE="/var/log/my_automation.log" LOCK_FILE="/tmp/my_automation.lock" TMP_DIR="/tmp/my_automation_$$" # ---------- توابع کمکی ---------- log() { echo "$(date '+%Y-%m-%d %H:%M:%S') - $*" | tee -a "$LOG_FILE" } cleanup() { if [[ -d "$TMP_DIR" ]]; then rm -rf "$TMP_DIR" log "پاکسازی دایرکتوری موقت انجام شد." fi rm -f "$LOCK_FILE" } trap cleanup EXIT # قفل یکبار اجرا (Prevent overlapping executions) if [[ -f "$LOCK_FILE" ]]; then log "اسکریپت قبلاً در حال اجراست. خروج." exit 0 else touch "$LOCK_FILE" fi # ---------- بدنهی اصلی ---------- log "شروع اتوماسیون..." mkdir -p "$TMP_DIR" # انجام عملیات اصلی در اینجا... # اگر خطایی رخ دهد، به لطف set -e، اسکریپت متوقف شده و trap پاکسازی را انجام میدهد. log "اتوماسیون با موفقیت پایان یافت." exit 0
این الگو را بهعنوان نقطهی شروع هر اسکریپت اتوماسیونی در نظر بگیرید تا از مشکلات رایج دور بمانید و قابلیت اطمینان بالایی داشته باشید.