Rsync (简体中文)

From ArchWiki
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
翻译状态:本文是 Rsync翻译。上次翻译日期:2021-8-20。如果英文版本有所更改,则您可以帮助同步翻译。

Tango-preferences-desktop-locale-modified.png这篇文章或章节的翻译不反映原文。Tango-preferences-desktop-locale-modified.png

原因:Out of sync with English page(在 Talk:Rsync (简体中文)# 中讨论)

rsync 是一个开源工具,可以进行快速的增量文件传输。

安装

安装 rsync 包。

必须在本地计算机和目标计算机上都安装 rsync

前端

  • Grsync — GTK 前端。
https://www.opbyte.it/grsync/ || grsync
  • gutback — 使用 Shell 编写的 rsync 包装。
https://github.com/gutenye/gutbackup || gutbackupAUR
  • JotaSync — 用于 rsync 的 Java Swing GUI,带有内置计划功能。
https://trixon.se/projects/jotasync/ || jotasyncAUR
  • luckyBackup — 用 C++ 编写的 Qt 前端。
http://luckybackup.sourceforge.net/index.html || luckybackupAUR

其他使用 rsync 的工具有 rdiff-backuposyncAUR


作为 cp/mv 的替代

rsync 可以作为 cpmv 命令的高级替代品,特别是对于较大文件的复制:

$ rsync -P source destination

其中 -P--partial --progress 选项的作用是相同的,该选项使得文件可以分块传输并显示传输过程中的进度。

您可能需要使用 -r/--recursive 选项递归到目录中传输。

可以像 cp 命令一样本地复制文件,但 rsync 令人激动的用途在于远程复制文件,例如在两个不同的主机之间。远程位置可以用主机加冒号进行指定:

$ rsync source host:destination

或者

$ rsync host:source destination

网络文件传输默认使用SSH协议,host 可以是真实的主机名或来自于 .ssh/config 的预先定义的配置文件/别名。

无论是本地或远程文件传输,rsync 首先会创建一个文件列表,其中含有接下来会被用于确定各个文件是否需要构建的信息(默认为文件大小和上次修改时间戳)。对于每个需要构建的文件,都会找出其所有块(长度S字节,不重叠,偏移量可由S整除)的一个弱校验和和一个强校验和。使用这一信息,一个大文件就可以由 rsync 构建,而无需传输整个文件。更详细的实际解释和详细的数学解释见 rsync 如何工作rsync 算法

要快速使用合理默认值,可以使用一些别名:

function cpr() {
  rsync --archive -hh --partial --info=stats1,progress2 --modify-window=1 "$@"
} 
function mvr() {
  rsync --archive -hh --partial --info=stats1,progress2 --modify-window=1 --remove-source-files "$@"
}
注意: 此处对校验和这一名词的使用与 --checksum 选项的行为不是相等价的。--checksum 选项影响的是在传输任何文件之前使用的决定是否跳过文件的启发式方法。在基于块的文件构建中一定会使用校验和,这是 rsync 传输文件的方法,与 --checksum 无关。

注意尾随下划

Arch 默认使用 GNU cp (GNU coreutils 的一部分)。 然而,rsync 遵循 BSD cp 的约定, 源目录后面带有一个斜杠“/”有着特定的处理。比如:

$ rsync -r source destination

创建一个有着 "source"内容的 "destination/source"目录,命令:

$ rsync -r source/ destination

把"source/"目录下的所有文件全部复制到"destination"目录下,而没有中间的子目录 - 就像你调用了:

$ rsync -r source/. destination

这与 GNU cp 的行为是不同的,在 GNU cp 中"source" 与 "source/" 意义相同 ("source/."则不然)。并且,一些shell在Tab补全目录名的时候自动追加尾部下划线。由于这些因素,新手或偶尔使用 rsync 的用户可能倾向于忘记 rsync 的不同行为,在命令行上留下了结尾的下划线,从而无意间造成混乱,甚至覆盖重要文件。

谨慎起见,可以使用包装脚本在调用 rsync 之前自动删除尾部斜杠:

#!/bin/zsh
new_args=();
for i in "$@"; do
    case $i in /) i=/;; */) i=${i%/};; esac
    new_args+=$i;
done
exec rsync "${(@)new_args}"

该脚本可以放在 path 中的某个位置,并在 shell 的 init 文件中指定别名为 rsync。

作为备份工具

rsync 协议可以很容易地用于备份,只传输自上次备份以来已更改的文件。本节将介绍一些简单的基于 rsync 的计划备份脚本,通常用于复制到可移动介质。

自动备份

以下面的脚本为例,该脚本放置于 /etc/cron.daily 目录下,如果 cron daemons 被正确安装和配置,它将每天运行。配置和使用 cron 是本文的范围之外。

首先,创建一个包含相应命令选项的脚本:

/etc/cron.daily/backup
#!/bin/bash
rsync -a --delete --quiet /path/to/backup /location/of/backup
-a
表示文件应被存档,这意味着他们的大部分特性被保留 (包括 ACLs, 硬链接或扩展属性,如capabilities)
--delete
指同步源文件的删除操作。

在这里,/path/to/backup 应该改成需要被备份的路径 (比如 /home),/location/to/backup 是备份应存放的位置 (比如 /media/disk).

最后,脚本必须是可执行的:

# chmod +x /etc/cron.daily/backup

通过 SSH 自动备份

如果是通过 SSH 备份到远程主机,改为使用此脚本:

/etc/cron.daily/backup
#!/bin/bash
rsync -a --delete --quiet -e ssh /path/to/backup remoteuser@remotehost:/location/of/backup
-e ssh
告诉rsync的使用SSH
remoteuser
远程主机 remotehost 上的用户名
-a
组中的所有这些选项 -rlptgoD (recursive, links, perms, times, group, owner, devices)
注意: 在每次增量备份的时候,Rsync 都会试着修改目标主机上所有先前备份过的文件以匹配源主机上的文件状态。这意味着在通过 SSH (并且使用 -a选项保留权限和所有者的时候)备份属于 root 的文件,需要目标主机的 root 权限。通常的办法是将 sshd 设置为允许使用公玥而不是密码登录 [1],并以 root 用户运行 rsync 命令。

使用 NetworkManager 自动备份

该脚本在网络连接后开始备份。

首先,创建一个包含相应命令选项的脚本:

/etc/NetworkManager/dispatcher.d/backup
#!/bin/bash

if [ x"$2" = "xup" ] ; then
        rsync --force --ignore-errors -a --delete --bwlimit=2000 --files-from=files.rsync /path/to/backup /location/to/backup
fi
-a
组中的所有选项 -rlptgoD recursive, links, perms, times, group, owner, devices
--files-from
从文件中读取到备份路径/path/to/backup的相对路径
--bwlimit
限 I/O 带宽;每秒千字节

这个脚本必须属于 root 用户 (参见 NetworkManager#Network services with NetworkManager dispatcher )。

使用 systemd 和 inotify 自动备份

注意:
  • 由于 inotify 和 systemd 的限制, (见 这个问题和回答),递归的文件系统监测是不可能的。即使你监测了一个目录和它的内容,它也不会递归子目录并检测其中的内容。你必须明确地指出所有需要检测的目录,就算这些目录是你已经监视的目录的子目录。
  • 以下设置基于一个 systemd/User 的实例。

相比于执行一个基于时间计划的定时备份,比如使用 corn,在每次文件变动的时候都执行备份也是可行的。 systemd.path 单元使用 notify 以监测文件系统,并且可以与 systemd.service 文件一同使用来在捕获到文件系统事件的时候启动任何进程(如你的 rsync 备份)。

首先,创建监测备份文件的 systemd.path 文件:

~/.config/systemd/user/backup.path
[Unit]
Description=Checks if paths that are currently being backed up have changed

[Path]
PathChanged=%h/documents
PathChanged=%h/music

[Install]
WantedBy=default.target

然后创建一个 systemd.service 文件,它将会在检测到变化的时候启动。默认情况下一个与 path 单元同名(backup.path),但以 .service 结尾的服务(backup.service)将会启动。

注意: 如果你需要运行多个 rsync 命令,使用 Type=oneshot。这将允许你指定多个 ExecStart= 参数,每个参数对应一个 rsync 命令。更简单的方法是写一个脚本来启动你所有的备份,就像 cron 脚本一样。
~/.config/systemd/user/backup.service
[Unit]
Description=Backs up files

[Service]
ExecStart=/usr/bin/rsync %h/./documents %h/./music -CERrltm --delete ubuntu:

现在只需要像普通的 systemd 服务那样,start/enable backup.path 它就会开始监测文件变化并且自动启动 backup.service 了。

每周差异备份

这个 rsync 选项很有用,每次运行时创建一个完整备份,并且每天在一个单独的目录中保留已修改文件的副本。

首先,创建一个包含相应命令选项的脚本:

/etc/cron.daily/backup
#!/bin/bash

DAY=$(date +%A)

if [ -e /location/to/backup/incr/$DAY ] ; then
  rm -fr /location/to/backup/incr/$DAY
fi

rsync -a --delete --quiet --inplace --backup --backup-dir=/location/to/backup/incr/$DAY /path/to/backup/ /location/to/backup/full/
--inplace
类似于 --partial ,更新本地已修改文件到目的地

快照备份

同样你也可以用这个工具来创建你文件的快照树(即一个有日期顺序的文件副本的目录)。这个快照树是基于硬链接的,这意味着只有修改过的文件才会占用空间。这也是苹果TimeMachine快照的原理。

实现这个功能的脚本很容易实现,使用 --link-dest 选项来创建增量快照,硬链接未改变的文件。

/usr/local/bin/snapbackup.sh
#!/bin/bash

# Basic snapshot-style rsync backup script 

# Config
OPT="-aPh"
LINK="--link-dest=/snapshots/username/last/" 
SRC="/home/username/files/"
SNAP="/snapshots/username/"
LAST="/snapshots/username/last"
date=`date "+%Y-%b-%d:_%T"`

# Run rsync to create snapshot
rsync $OPT $LINK $SRC ${SNAP}$date

# Remove symlink to previous snapshot
rm -f $LAST

# Create new symlink to latest snapshot for the next backup to hardlink
ln -s ${SNAP}$date $LAST 

这里必须有一个已经存在的完整备份的符号链接,以作为 --link-dest 的目标。如果这个备份被删除,那么就需要重新创建一个新的指向完整备份的符号链接。如果 --link-dest 没有找到有效的符号链接,rsync将继续复制所有文件,而不只复制变化。

一个更加复杂一点的版本每次保留一个最新的完整备份到 $SNAP/latest 。若自上次完整备份起有一定数量的文件发生了变化,那么脚本将创建一个新的备份到 $SNAP/$DATETAG ,并使用 cp -al 来硬链接未改变的文件:

/usr/local/bin/rsnapshot.sh
/usr/local/bin/rsnapshot.sh
#!/bin/bash

## my own rsync-based snapshot-style backup procedure
## (cc) marcio rps AT gmail.com

# config vars

SRC="/home/username/files/" #dont forget trailing slash!
SNAP="/snapshots/username"
OPTS="-rltgoi --delay-updates --delete --chmod=a-w"
MINCHANGES=20

# run this process with real low priority

ionice -c 3 -p $$
renice +12  -p $$

# sync

rsync $OPTS $SRC $SNAP/latest >> $SNAP/rsync.log

# check if enough has changed and if so
# make a hardlinked copy named as the date

COUNT=$( wc -l $SNAP/rsync.log|cut -d" " -f1 )
if [ $COUNT -gt $MINCHANGES ] ; then
        DATETAG=$(date +%Y-%m-%d)
        if [ ! -e $SNAP/$DATETAG ] ; then
                cp -al $SNAP/latest $SNAP/$DATETAG
                chmod u+w $SNAP/$DATETAG
                mv $SNAP/rsync.log $SNAP/$DATETAG
               chmod u-w $SNAP/$DATETAG
         fi
fi

懒得手动运行的话,你也可以把这个脚本放到 systemd (简体中文)/Timers (简体中文) ,作为单元运行。

全盘系统备份 

本节是关于使用 rsync 来创建一份整个 / 树的副本,其中不包含特定的几个文件夹。此方法相较于使用 dd 来进行硬盘克隆要更佳,因为它允许你在使用不同大小、分区表和文件系统的存储设备间传输;也比 cp -a 更好,因为它允许你对文件权限、属性、访问控制列表扩展属性有更好的掌握。

rsync 在系统运行时亦可进行备份,但传输期间改变的文件可能不会被备份。这可能会造成使用这些文件的程序的一些未知错误或未定义行为。

这种方法对于将现有的已安装系统迁移到新的硬盘或SSD上非常有效。

在 root 权限下运行此命令,以确保你能访问到所有系统文件,并且拥有权限:

# rsync -aAXHv --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","/lost+found"} / /path/to/backup

通过使用 -aAX 选项集,文件以归档模式传输,确保符号链接、设备、权限、所有权、修改时间、ACLs和扩展属性得以保留,前提是目标文件系统支持这一功能。选项 -H 保留了硬链接,但会使用更多的内存。

--exclude 选项使符合给定模式的文件被排除。在上述命令中,/devproc/sys/tmp/run 等目录被包括在内,但这些目录的内容被排除在外。这是因为它们在系统启动时才会被填入内容,但这些目录本身不会被创建。 /lost+found 是针对文件系统的。上面的命令依赖于 bashzsh 中可用的括号扩展。当使用不同的 shell 时,应该手动重复 --exclude 模式。加入排除选项可以避免被 shell 误扩展,例如,在通过 SSH 备份时,这是必要的。以 * 结尾的排除路径可以确保目录本身在不存在的情况下被创建。 份,就像 cron 脚本一样。}}