scp и rsync
scp копирует файлы через SSH. rsync -avz передаёт только изменённые байты и сжимает данные; rsync -a --delete зеркалирует каталог и удаляет на удалённой стороне файлы, отсутствующие в источнике — опасная ловушка, если не проверить --dry-run заранее.
Перемещение файлов на удалённые серверы и обратно — ежедневная операция: загрузить артефакт сборки, скачать лог-файл, синхронизировать каталог. scp справляется с простыми однократными копированиями через SSH. rsync обрабатывает всё остальное: частичные передачи с возобновлением после прерывания, инкрементальные синхронизации, передающие только изменённые байты, и зеркалирование каталогов с удалением на назначении файлов, которых больше нет в источнике. Этот последний функционал — --delete — и есть место, где операторы устраивают самые серьёзные самоинфлигированные аварии. rsync, направленный на неправильную цель с --delete, молча сотрёт файлы без какого-либо запроса подтверждения. Этот урок даёт тебе ментальную модель для уверенного использования rsync и привычку всегда сначала запускать --dry-run.
После этого урока ты сможешь копировать файлы через scp, использовать rsync -avz для эффективных инкрементальных передач, понимать правило завершающего слеша, определяющее, копирует ли rsync каталог или его содержимое, использовать rsync -a --delete для зеркалирования каталога, зная об опасности, и всегда предварительно просматривать деструктивные синхронизации через --dry-run.
scp копирует файлы через SSH, используя ту же аутентификацию, что и ssh. Синтаксис повторяет cp, но допускает удалённые пути в форме user@host:path:
# локально → удалённо
scp build/app.tar.gz deploy@server.example:/var/deploys/
# удалённо → локально
scp deploy@server.example:/var/log/app.log ./app.log
# удалённо → удалённо (через твою локальную машину как ретранслятор)
scp deploy@203.0.113.10:/tmp/data.csv deploy@198.51.100.20:/tmp/data.csvscp достаточен для одноразового копирования файлов. Инкрементальной передачи у него нет — он всегда копирует файл целиком. Для больших или часто обновляемых файлов rsync эффективнее.
rsync -avz — основной инструмент для эффективных передач. Флаги:
-a(archive): сохраняет права, временные метки, симлинки, владельца, группу. Рекурсия включена.-v(verbose): печатает каждый передаваемый файл.-z(compress): сжимает данные при передаче — полезно на медленных каналах, избыточно в локальных сетях или для уже сжатых файлов.
rsync -avz ./dist/ deploy@server.example:/var/www/html/rsync вычисляет контрольную сумму каждого файла и передаёт только изменившиеся. При повторном запуске без изменений завершается почти мгновенно. Это ключевое отличие от scp.
Вывод показывает, что было передано:
sending incremental file list
index.html
assets/app.42a1b.js
sent 48,231 bytes received 542 bytes 9,754.60 bytes/secЗавершающий слеш в источнике меняет то, что копирует rsync — это самая распространённая ошибка. Семантика завершающего слеша rsync:
# СО завершающим слешем — копировать СОДЕРЖИМОЕ dist/ в html/
rsync -avz ./dist/ deploy@server.example:/var/www/html/
# Результат: html/index.html, html/assets/app.js ...
# БЕЗ завершающего слеша — копировать САМ каталог dist/ в html/
rsync -avz ./dist deploy@server.example:/var/www/html/
# Результат: html/dist/index.html, html/dist/assets/app.js ...У пути назначения нет такой неоднозначности — он всегда контейнер. Правило: если хочешь, чтобы содержимое src/ попало прямо в dst/, ставь завершающий слеш на источнике. Если хочешь создать dst/src/ с файлами — не ставь. Проверяй своё намерение перед каждым вызовом rsync.
rsync -a --delete зеркалирует каталог — и удаляет на удалённой стороне файлы, отсутствующие в источнике. Это самая мощная и самая опасная комбинация флагов rsync:
rsync -avz --delete ./dist/ deploy@server.example:/var/www/html/--delete удаляет из удалённого /var/www/html/ все файлы, которых нет в локальном ./dist/. Это именно то, что нужно для зеркала деплоя: удалённая сторона точно отражает источник, без накопления устаревших файлов. И это именно то, как удаляются важные файлы, если перепутать источник или назначение.
Распространённые ошибки:
- Неправильный каталог источника (на уровень выше):
rsync -a --delete ./ deploy@server.example:/var/www/html/— удалит в html/ всё, чего нет в текущем каталоге. - Переставленные источник и назначение: удалённая сторона становится источником, локальная — назначением; файлы удаляются локально.
- Пропущенный завершающий слеш при необходимости: копирование на неправильный уровень вложенности.
Всегда используй --dry-run перед любой деструктивной синхронизацией. --dry-run (или -n) печатает точно, что сделает rsync, без передачи или удаления чего-либо:
rsync -avz --delete --dry-run ./dist/ deploy@server.example:/var/www/html/Вывод включает:
deleting old-feature/
deleting old-feature/index.html
index.html
assets/app.42a1b.jsЧитай этот вывод внимательно. Если видишь строки deleting, которых не ожидал — останавливайся. Привычка: каждый раз при добавлении --delete сначала запускай с --dry-run, проверяй список удалений, затем запускай без него. Эта одна дисциплина предотвращает целую категорию инцидентов с потерей данных.
Безопасный деплой статического сайта с rsync.
# Шаг 1: всегда сначала dry-run
rsync -avz --delete --dry-run \
./dist/ \
deploy@server.example:/var/www/html/
# Читай вывод. Проверь, что удаления ожидаемы.
# Если вывод выглядит правильно, продолжай:
# Шаг 2: реальная синхронизация
rsync -avz --delete \
./dist/ \
deploy@server.example:/var/www/html/Дополнительные полезные флаги для деплоя:
# --exclude: пропускать файлы по шаблону
rsync -avz --delete --exclude='.env' ./dist/ deploy@server.example:/var/www/html/
# --checksum: сравнивать по содержимому, не только по mtime+size (медленнее, но точнее)
rsync -avz --checksum ./dist/ deploy@server.example:/var/www/html/
# --progress: показывать прогресс по каждому файлу (полезно для больших файлов)
rsync -avz --progress ./large-assets/ deploy@server.example:/var/assets/▸Почему это работает
На macOS предустановленный rsync — это старая BSD-версия (2.6.x). В ней отсутствуют некоторые полезные флаги, включая --info, частичная поддержка --progress и некоторое поведение --filter. Установи GNU-версию через Homebrew (brew install rsync) при возникновении ограничений. Основные флаги, используемые в этом уроке (-avz, --delete, --dry-run, --exclude), работают идентично в обеих версиях.
▸Частая ошибка
rsync --delete не запрашивает подтверждение и не имеет отмены. Файлы, удалённые с удалённой стороны, пропали, если у тебя нет отдельного резервного копирования. Привычка --dry-run не опциональна — это единственный механизм безопасности. Вторая усиливающая привычка: всегда указывай источник и назначение как полные явные пути в скриптах деплоя, никогда относительные вроде ./ в качестве источника. ./ не в том каталоге уничтожит не то, что нужно.
Ты запускаешь: rsync -avz ./dist deploy@server.example:/var/www/html/. Где окажутся файлы на сервере?
scp копирует файлы через SSH за один раз — хорош для простых разовых передач, без инкрементальной логики. rsync -avz передаёт только изменённые байты, сжимает данные и сохраняет метаданные — правильный инструмент для повторяющихся синхронизаций. Правило завершающего слеша: со слешем на источнике rsync копирует содержимое в назначение; без слеша — копирует сам каталог. --delete заставляет rsync удалять удалённые файлы, отсутствующие в источнике — мощно для зеркал деплоя, опасно при ошибке в источнике или назначении. --dry-run — обязательная проверка безопасности перед любым вызовом с --delete: читай вывод, проверяй удаления, затем запускай по-настоящему.
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.