rsync دستوری در لینوکس برای کپی و همگامسازی فایلها و پوشههاست، چه روی یک سیستم و چه بین دو سرور از طریق SSH. فرقش با cp و scp این است که فقط تفاوتها را منتقل میکند: بار دوم که اجرایش کنید، فایلهایی که تغییر نکردهاند دوباره فرستاده نمیشوند. رایجترین شکل استفاده این است:
rsync -avzh --progress /var/www/app/ user@server:/var/www/app/
برای کنار گذاشتن بعضی فایلها هم --exclude را اضافه میکنید، مثلاً --exclude='node_modules/' --exclude='*.log'. در ادامه معنی هر گزینه، قاعدهی مهم «اسلش آخر مسیر» که بیشترین اشتباه را به بار میآورد، تمرین با --dry-run، خطر --delete و چند سناریوی واقعی را میبینیم. مثالها برای Ubuntu 24.04 نوشته شدهاند.
نصب و پیشنیاز
rsync روی بیشتر توزیعها از قبل نصب است. اگر نبود:
sudo apt install -y rsync
نکتهای که زیاد فراموش میشود: برای انتقال بین دو سرور، rsync باید روی هر دو طرف نصب باشد؛ چون rsync سمت مقصد را از طریق SSH اجرا میکند و دو طرف با هم مقایسه میکنند. اگر خطای rsync: command not found از سمت مقصد گرفتید، دلیلش همین است.
برای انتقال از طریق SSH هم بهتر است ورود با کلید را راه انداخته باشید تا هر بار رمز نخواهد؛ جزئیاتش در کلید SSH و اتصال به سرور آمده است.
گزینههای پایه: -a -v -z -h --progress
| گزینه | معنی |
|---|---|
-a |
حالت آرشیو: پوشهها را بازگشتی کپی میکند و دسترسیها، زمان تغییر، لینکهای نمادین، مالک و گروه را حفظ میکند |
-v |
گزارش فایلهایی که منتقل میشوند |
-z |
فشردهسازی داده در مسیر انتقال |
-h |
اعداد خوانا (K، M، G) |
--progress |
نمایش پیشرفت هر فایل |
-n یا --dry-run |
فقط نشان میدهد چه کار میکرد، بدون تغییر |
-P |
معادل --partial --progress |
-a تقریباً همیشه لازم است. بدون آن rsync پوشهها را بازگشتی کپی نمیکند و زمان تغییر فایلها را هم حفظ نمیکند، و بار بعد چون زمانها فرق دارند، دوباره همهچیز را مقایسه میکند. توجه کنید که حفظ مالک فایلها فقط وقتی ممکن است که سمت مقصد با root اجرا شود.
-z روی اینترنت مفید است، ولی برای فایلهای از پیش فشرده (.gz، .zip، تصویر، ویدیو) سودی ندارد و فقط CPU میسوزاند. در شبکهی محلی سریع هم معمولاً بدون آن سریعتر است.
اگر بهجای پیشرفت هر فایل، پیشرفت کل انتقال را میخواهید، به جای --progress از --info=progress2 استفاده کنید.
قاعدهی اسلش آخر مسیر
مهمترین نکتهی rsync همین است. اسلش آخرِ مسیر مبدأ تعیین میکند خود پوشه کپی شود یا محتوایش:
# با اسلش: محتوای site داخل backup ریخته میشود
rsync -a site/ backup/
# نتیجه: backup/index.php backup/css/ ...
# بدون اسلش: خود پوشهی site داخل backup ساخته میشود
rsync -a site backup/
# نتیجه: backup/site/index.php backup/site/css/ ...
یک راه به یاد سپردن: site/ یعنی «آنچه داخل site است» و site یعنی «خود site».
اسلش آخرِ مسیر مقصد فرق چندانی ایجاد نمیکند؛ backup و backup/ هر دو یک پوشهاند و اگر نباشد ساخته میشود. ولی برای یکدستی، بهتر است همیشه شکل src/ dest/ را بنویسید تا با یک نگاه معلوم باشد دو پوشه را همگام میکنید.
اشتباه رایج: کسی rsync -a /var/www/app user@server:/var/www/app میزند و بعد روی سرور با /var/www/app/app روبهرو میشود. اگر --delete هم در کار بود، نتیجه بدتر هم میشود. برای همین بخش بعد مهم است.
همیشه اول --dry-run
قبل از هر rsync که مطمئن نیستید، همان دستور را با -n (یا --dry-run) اجرا کنید. هیچ چیز تغییر نمیکند، فقط فهرست کارهایی که انجام میشد را میبینید:
rsync -avhn --delete /var/www/app/ user@server:/var/www/app/
با -i (--itemize-changes) هم برای هر فایل یک کد کوتاه میگیرید که میگوید چرا منتقل میشود (فایل جدید، تغییر اندازه، تغییر زمان و …). خطوطی که با deleting شروع میشوند را با دقت بخوانید. وقتی خروجی همان بود که انتظار داشتید، -n را بردارید و دوباره اجرا کنید.
--delete و خطرش
بهطور پیشفرض rsync فقط اضافه و بهروز میکند؛ اگر فایلی را از مبدأ پاک کنید، در مقصد میماند. --delete این را عوض میکند: هر چیزی که در مبدأ نیست، از مقصد پاک میشود تا دو طرف دقیقاً یکسان شوند.
این گزینه برای انتشار یک نسخهی تمیز مفید است، ولی سه خطر جدی دارد:
- جابهجا نوشتن مبدأ و مقصد. اگر جای دو مسیر را اشتباه بنویسید، rsync نسخهی قدیمی را روی نسخهی جدید همگام میکند و فایلهای جدید را پاک میکند.
- مبدأ خالی یا اشتباه. اگر مبدأ به هر دلیلی خالی باشد (دیسک mount نشده، متغیر خالی در اسکریپت)،
--deleteکل مقصد را خالی میکند. - پشتیبانگیری. اگر فایلی در مبدأ خراب یا پاک شود،
--deleteهمان خرابی را به نسخهی پشتیبان هم منتقل میکند. برای همین در مقالهی پشتیبانگیری ۳-۲-۱ عمداً از--deleteاستفاده نکردهایم.
اگر --delete لازم است، چند لایه احتیاط بگذارید:
# فایلهای پاکشده یا بازنویسیشده را به جای حذف، کنار بگذار
rsync -a --delete --backup --backup-dir=/var/backups/rsync-$(date +%F) \
/var/www/app/ /srv/app-mirror/
# بیش از ۱۰۰ فایل پاک نکن؛ بقیهی حذفها رد میشوند و rsync با کد خطا تمام میشود
rsync -a --delete --max-delete=100 /var/www/app/ /srv/app-mirror/
--backup-dir اگر مسیر نسبی بدهید، نسبت به مقصد حساب میشود؛ برای همین مسیر مطلق بنویسید.
کنار گذاشتن فایلها: --exclude و --exclude-from
rsync -avh \
--exclude='.git/' \
--exclude='node_modules/' \
--exclude='*.log' \
--exclude='/storage/logs/' \
./ user@server:/var/www/app/
چند قاعده دربارهی الگوها:
- الگوها نسبت به ریشهی انتقال (همان مبدأ) سنجیده میشوند، نه نسبت به ریشهی سیستم.
- الگوی بدون
/در ابتدا در هر عمقی پیدا میشود:node_modules/هر پوشهای با این نام را در هر جای درخت کنار میگذارد. /در ابتدا الگو را به ریشهی انتقال میچسباند:/storage/logs/فقط همان مسیر را، نهvendor/x/storage/logs/را./در انتها یعنی «فقط پوشه».- الگو را داخل کوتیشن بگذارید تا شل خودش
*را باز نکند.
وقتی فهرست بلند شد، آن را در یک فایل بنویسید:
.git/
.env
node_modules/
/storage/logs/
/storage/framework/cache/
*.log
rsync -avh --exclude-from=deploy-exclude.txt ./ user@server:/var/www/app/
اگر --delete هم دارید، بدانید فایلهای excludeشده در مقصد پاک نمیشوند؛ یعنی .env سرور سالم میماند. مگر اینکه --delete-excluded بدهید، که آنها را هم پاک میکند.
انتقال از طریق SSH با پورت غیراستاندارد
اگر SSH سرور روی پورتی غیر از ۲۲ است، با -e دستور SSH را به rsync بدهید:
rsync -avzh -e "ssh -p 2222" /var/www/app/ deploy@server:/var/www/app/
# با کلید مشخص
rsync -avzh -e "ssh -p 2222 -i ~/.ssh/deploy_key" ./ deploy@server:/var/www/app/
جهت انتقال را هم میشود برعکس کرد و از سرور به سیستم خودتان کشید:
rsync -avzh -e "ssh -p 2222" deploy@server:/var/backups/db/ ./db-backups/
راه تمیزتر این است که پورت و کاربر و کلید را در ~/.ssh/config تعریف کنید؛ آنوقت rsync هم بدون -e از همان تنظیمات استفاده میکند و فقط مینویسید rsync -avzh ./ app:/var/www/app/.
ادامهی انتقال فایلهای بزرگ
اگر وسط انتقال یک فایل چند گیگابایتی اتصال قطع شود، rsync بهطور پیشفرض بخش منتقلشده را دور میاندازد. با --partial آن بخش نگه داشته میشود و اجرای بعدی از روی آن ادامه میدهد:
rsync -avh -P -e "ssh -p 2222" big-dump.sql.gz deploy@server:/var/backups/
-P همان --partial --progress است. اگر نمیخواهید فایل نیمهکاره با نام اصلی در مقصد دیده شود (مثلاً برنامهای پوشه را میپاید)، --partial-dir=.rsync-partial را اضافه کنید تا تکهها در پوشهای جدا بمانند. برای انتقالهای خیلی طولانی هم rsync را داخل tmux اجرا کنید تا قطع SSH آن را نکشد.
rsync، scp یا cp؟
| معیار | rsync |
scp |
cp |
|---|---|---|---|
| بین دو سیستم | بله (SSH) | بله (SSH) | خیر |
| فقط انتقال تغییرات | بله | خیر، همیشه کامل | خیر |
| ادامهی انتقال قطعشده | بله (--partial) |
خیر | خیر |
| exclude | بله | خیر | خیر |
| همگامسازی با حذف | بله (--delete) |
خیر | خیر |
| dry-run | بله | خیر | خیر |
| نیاز به نصب در مقصد | بله | خیر | — |
| مناسب برای | پوشهها، تکرار، پشتیبان | یک فایل سریع | کپی محلی ساده |
قاعدهی سرانگشتی: برای یک فایل که یک بار جابهجا میشود scp کافی است. هر چیزی که پوشه است، تکرار میشود یا بزرگ است، کار rsync است.
جمعبندی
- شکل پایه را به خاطر بسپارید:
rsync -avh --progress src/ dest/. - اسلش آخر مبدأ یعنی «محتوا»، بدونش یعنی «خود پوشه».
- هر دستور تازه را اول با
-nاجرا کنید، بهخصوص اگر--deleteدارد. --deleteرا در پشتیبانگیری کنار بگذارید و جاهای دیگر با--backup-dirیا--max-deleteمهارش کنید.- فهرست exclude را در فایل نگه دارید و پورت SSH را در
~/.ssh/config.
اگر rsync را در یک اسکریپت زمانبندیشده میگذارید، اسکریپتنویسی bash و کرون جاب در لینوکس قدمهای بعدیاند.