У замечательной и очень популярной программы rsync есть множество удобных опций и необычайных способностей, вот только их не так-то просто найти. Ну, если только вы не готовы прочитать весь мануал от корки до корки.

Попробую сберечь вам немного времени на чтение мануала и рассказать о самом важном и полезном. Простыми словами, как есть.

Зачем нужен rsync?

Зачем пользоваться rsync если есть привычные cp и scp, спросите вы.

  1. В одной части rsync удобней и лучше упомянутых аналогов потому что он дает вам возможность увидеть все что он сделает при копировании файлов до непосредственно операции копирования.

  2. В другой части - rsync по умолчанию копирует только новые и изменившиеся файлы, и даже не только файлы - а отдельные их части, делая это жестоко и эффективно.

Только этих двух причин достаточно чтобы в своей ежедневной работе забыть про cp, променяв эту команду и ее аналоги на rsync.

Принцип применения

Если мы говорим о простом копировании файлов, то первым делом всегда стоит сделать пробный прогон (ключ -n) в режиме с показом подробностей (-v):

rsync -avn source example.com:destination

В этом режиме rsync покажет список файлов, которые он скопирует. Будут скопированы только новые и изменившиеся файлы. Вы сможете убедиться что копируется сам каталог, а не содержимое, или что копируется именно содержимое.

После того как вы убедились что копируется именно то, что вы хотите, можно запускать самое настоящее копирование:

rsync -av source example.com:destination

В этой команде ключ -a подразумевает рекурсивное копирование всех файлов и каталогов включая их атрибуты, такие как дата создания и дата изменения. Ключ -v даст вам подробный отчет о работе по мере выполнения и по окончании.

Правила копирования каталогов

С одной стороны правила очень простые.

  1. Если в конце пути до именованного источника нет слеша, то скопируется сам каталог.

    $ rsync -avn path/to/source example.com:destination
    sending incremental file list
    source/
    source/example.html
    ...
    
  2. Если слеш есть, или источник указывает на каталог без имени, то скопируется содержимое каталога.

    $ rsync -avn path/to/source/ example.com:destination
                              ^^^
    sending incremental file list
    example.html
    ...
    
    # Что эквивалентно такой команде:
    $ cd path/to/source; rsync -avn . example.com:destination
    

С другой стороны вполне можно в пылу забыть что и как, скопировав содержимое каталога вместо самого каталога, получив кучу хлопот с удалением лишних файлов, появившихся из ниоткуда с датами в прошлом (ключик -a, помните?).

Потому лучше всегда следовать обычному сценарию с предварительной проверкой операции.

Некоторые полезные ключи

Сначала поговорим об опциях которые вам будет здорово знать без шпор и шпаргалок.

  1. Ключ -P включает сразу несколько опций. С этим ключем rsync и покажет процесс копирования отдельных файлов, и продолжит копирование если оно было прервано. Особо эта опция пригождается при копировании больших файлов. Ключ -P нужно указывать каждый раз, иначе rsync удалит файлы которые не успели полностью закачаться.

  2. Если вы копируете файлы с какого-то очень занятого, или слабого, сервера, то можно исключить трату ресурсов процессора на вычисление изменившихся частей файлов, скопировав их целиком. Для этого нужен ключ -W.

  3. Если вам хочется знать сколько всего, по мнению rsync, осталось работать, вам нужен ключ --info=progress2. Если вы копируете целую файловую систему, то этот ключ, будучи использован сам по себе, вас разочарует: информация об итоговом объёме будет постоянно обновляться. Это происходит потому что rsync не пытается считать всю файловую систему до того как начнёт копирование, а делает обе задачи сразу.

    Но не отчаивайтесь! Если вы хотите знать точно сколько осталось работать с самого начала, то можно отключить последовательное сканирование ключём --no-inc-recursive или, короче, --no-i-r.

    $ rsync -ah --partial --info=progress2 --no-i-r source example.com:destination
            623.38M   0%   82.23MB/s    0:11:10
    

    Ключи выше есть начиная с версии 3.1.0, то есть уже работают в Debian stable.

  4. Если требуется не просто скопировать файлы, а полностью синхронизировать содержимое каталогов, удалив лишние файлы, при этом вам почему-то не с руки синхронизировать файлы с помощью Git, то пригодится ключ --delete (или эквивалентный ему --del).

    С этим ключём rsync удалит лишние файлы из каталого-назначения.

    $ rsync -avn --delete source example.com:destination
    sending incremental file list
    deleting source/bad.txt
    source/
    source/test.txt
    

    Ключ -n в команде выше был оставлен намеренно.

Дальше я рассмотрю в деталях более интересные, хоть более редко используемые, опции.

О сжатии замолвим слово

Вопреки популярному заблуждению от использования сжатия внутри rsync (ключ -z) больше вреда, чем пользы. Дело в том что всюду используемый OpenSSH уже с версии конца 2005 года по умолчанию использует сжатие передаваемых данных. Сами понимаете, сжатие уже сжатых данных только лишь использует ресурсы процессора, не уменьшая объем передаваемых данных.

В том, что при соединении с вашим сервером уже используется сжатие, можно убедиться так:

$ ssh -v user@example.com false 2>&1 | grep compression
debug1: Enabling compression at level 6.

Если эта команда не покажет, в числе прочих, такую строчку про включение сжатия как выше, то может быть вам стоит использовать сжатие. Стоит проверить что от сжатия есть толк. Особенно это касается маломощных устройств с быстрым соединением: вашему NAS может быть быстрее скопировать что-то по гигабитному линку без сжатия, чем пытаться обогнать гигабитную сеть маломощным процессором.

К счастью, rsync достаточно умен чтобы не использовать сжатие если вы копируете файлы локально, из каталога на диск и т.п.

Копируем частично

Наверняка вам когда-нибудь понадобится чтобы rsync пропускал некоторые файлы при копировании.

В самом простейшем случае вам нужно чтобы rsync не копировал файлы разных систем контроля версий, включая каталог вроде .svn и .git. В этой задаче вам не нужно ничего кроме ключа -C (или --cvs-exclude в полной форме). Так файлы большинства популярных VCS будут проигнорированы будто их нет. Не забываем использовать -n при первом запуске.

rsync -nC example.com:source destination

Может получиться так что вы, по ошибке, скопируете кучу таких файлов от VCS. В этом случае для получения чистой копии вам пригодится ключ --delete-excluded, с которым все исключенные файлы будут удалены.

rsync -nC --delete-excluded example.com:source destination

Исключаем через .rsync-filter

Если нужные более гибкие правила, что особенно актуально если копирование делается регулярно, то лучше не мелочиться и оформить все исключения в файле .rsync-filter.

$ cat source/.rsync-filter 
- test.bin
- *.tmp
- /.cache
- /example/
- /**/Trash/
- /.mozilla/firefox/*/Cache/
+ Projects/**/Trash/

Для исключения чего-либо из списка на перенос нужно добавить в этот файл строчку с правилом (- или + в начале строки).

  1. Если нужно исключить конкретный файл, где бы он ни был в любом каталоге ниже по иерархии, то укажем просто имя файла.

    # никакой файл test.bin не будет скопирован
    - test.bin
    # все файлы .tmp будут пропущены 
    - *.tmp
    
  2. Если нужно исключить файл или каталог относительно каталога в котором находится .rsync-filter, то укажем со слешем в начале:

    # не будет скопирован каталог или файл .cache, но будут скопированы foo/.cache и foo/bar/.cache
    - /.cache
    # не будет скопирован каталог example, но будет скопирован файл example
    - /example/
    
  3. В правилах звездочка соответствует любым символам кроме слеша, а две звездочки соответствуют вообще любым символам:

    # будут пропущены каталоги .local/share/Trash/ и Documents/example/Trash/
    - /**/Trash/
    # не будет пропущен каталог .mozilla/firefox/abcd.profile/ext/Cache/
    # но будет пропущен каталог .mozilla/firefox/abcd.profile/Cache/
    - /.mozilla/firefox/*/Cache/
    
  4. Наконец, если нужно чтобы какие-то файлы всё-таки копировались, не смотря на ранее заданные правила, то их можно отметить правилом + в начале строки.

    # каталог Projects/Example/layout/Trash/ будет скопирован
    +  Projects/**/Trash/
    

Файлы .rsync-filter команда rsync умеет искать по всей структуре каталогов будучи запущена с ключём -F.

Если нужно чтобы сами эти файлы не копировались, то нужно указать этот ключ два раза так:

$ rsync -avFFn source example.com:destination
sending incremental file list
source/
source/example.html
source/tmp/
source/tmp/foo.bin

sent 174 bytes  received 30 bytes  408.00 bytes/sec
total size is 18,400  speedup is 90.20 (DRY RUN)

Как видите, лишние файлы не скопировались:

$ ls source/.rsync-filter source/foo.tmp 
source/foo.tmp  source/.rsync-filter
$ cat source/.rsync-filter 
- *.tmp

Ограничим rsync по ssh

Случается нужно разрешить работу rsync по ssh, удалённо и без пароля, только определённого для каталога и хоста, исключив копирование чего-либо в другие места или из других мест.

Например, вы хотите чтобы можно было скопировать файлы на сервер backup.example.com только с хоста server.example.com, только и только в каталог backup-example, и только с этими опциями:

$ rsync -aW --del source/ backup.example.com:destination/backup-example/

То сначала нужно получить команду, которую rsync выполняет при вызове ssh на удаленном хосте:

$ rsync -e "ssh -t -v" -aW --del source/ backup.example.com:destination/backup-example/ 2>&1 | grep command
debug1: Sending command: rsync --server -lWogDtpre.iLsfxC --delete-during . destination/backup-example/

Соответственно, в ~/.ssh/authorized_keys на example.com следует добавить для известного ssh ключа запуск этой команды по умолчанию при подключении:

from="server.example.com",command="rsync --server -lWogDtpre.iLsfxC --delete-during . destination/backup-example/",no-pty,no-port-forwarding ssh-rsa AAAA... # дальше ваш ключ

Таким образом, даже если при запуске rsync будут указаны какие-то другие опции, на сервере-назначении всё равно выполнится команда rsync соответствующая исходным, заданным вами, опциям и настройкам.

Если нужно чтобы ваш бекап нельзя было перезаписать или удалить на сервере назначения, то опцию --del следует заменить на --ignore-existing.

Машина времени

Те пользователи macOS и OS X, что делают бэкап, наверняка оценили работу Time Machine. Эта программа позволяет буквально в два клика вернуться к прошлой версии любого файла. Не смотря на все красивости, Time Machine не делает ничего такого чего мы не можем сделать с помощью rsync.

#!/bin/bash
set -o nounset -o errexit
cd $(dirname "$0")
date=$(date --iso-8601=seconds)
test -L latest || ln -s "$date" latest
rsync --delete-excluded --prune-empty-dirs --archive -F --link-dest=../latest "$@" "./$date"
rm latest
ln -s "$date" latest

Скрипт следует положить в корень того диска или каталога, куда следует делать бэкапы.

Запускать с указанием единственного аргумента: каталога с исходными файлами. Например, так.

/mnt/backups/backup /home

После нескольких запусков получается такая структура каталога:

2017-02-08T22:05:04+09:00
2017-02-08T22:10:05+09:00
2017-02-08T22:15:05+09:00
2017-02-08T22:20:06+09:00
2017-02-08T22:25:05+09:00
2017-02-08T22:30:04+09:00
latest -> 2017-02-08T22:30:04+09:00

При этом latest указывает на самый последний бэкап.

В каждом из каталогов содержится слепок того, что было в исходном каталоге в момент копирования. Можно подумать что и место на диске занято пропорционально количеству копий, однако это не так.

$ du -sh /mnt/backups
4,5M    /mnt/backups

$ du -sh /home
3,8M    /home

Всё множество копий занимает лишь немного больше места чем исходный каталог. Место уходит на изменившиеся файлы.

Если ничего не менялось, то место всё равно расходуется на создание каталогов, которые нельзя хранить как жесткие ссылки.

$ du -hs 2017-02-08T22:20:06+09:00 2017-02-08T22:25:05+09:00 2017-02-08T22:30:04+09:00
3,8M    2017-02-08T22:20:06+09:00
136K    2017-02-08T22:25:05+09:00
136K    2017-02-08T22:30:04+09:00

Такая существенная экономия возможна благодаря упомянутым жестким ссылкам, которые rsync делает для файлов, не изменившихся с последнего копирования.

$ stat -c '%i' 2017-02-08*/example.txt | uniq
31819810

У одинаковых, не менявшихся, файлов будет один и тот же inode.

Конечно, по возможной экономии места на диске такому способу резервного копирования далеко до специальных программам, таких как Borg или Restic.