参考资料:骏马金龙的rsync系列。该博主的博文质量很好,推荐大家关注。

环境

操作系统:CentOS Linux release 7.5.1804 (Core)

软件:rsync  version 3.1.2  protocol version 31

前言

rsync可以实现scp的部分远程复制功能(支持本地到远程复制和远程到本地复制,但是不支持远程到远程复制;scp可以支持远程到远程复制)、cp的本地复制功能、rm的删除功能和“ls -l”的显示详细信息文件列表功能。

注意,rsync的初衷是实现两端主机之间的数据同步,上述功能只是作为辅助,并且其实现方式是与scp/cp/rm/ls这些命令不同的。

rsync有自己的一套算法以及算法实现机制,日常使用的话可以无需了解。但是,如果想要看懂rsync的man手册或者官方文档,想要通过-vvvv选项看懂rsync的执行过程,那么还是有必要了解rsync的原理的。

同步基础

同步涉及到了源文件(源主机、发送端)和目标文件(目标主机、接收端)、本地主机和远程主机、以及以哪边的主机上的文件为基准的概念。以谁为基准,就是以谁作为源文件。

  • 想让本地主机上的文件和远程主机上的文件保持同步。则以远程主机上的文件为基准(即作为源文件),将其拉取到本地主机覆盖本地主机的文件(即作为目标文件)。
  • 想让远程主机上的文件和本地主机上的文件保持同步。则以本地主机上的文件为基准(即作为源文件),将其推送到远程主机覆盖远程主机的文件(即作为目标文件)。
  • 本地主机和远程主机,既可以作为源主机/发送端,也可以作为目标主机/接收端,看具体的情况而定。

同步的过程,还涉及到其他的问题。

  • 是否删除源主机上没有但是目标主机上多出来的文件?
  • 目标文件的mtime比源文件的mtime更(gèng)新的时候,是否覆盖?
  • 遇到字符链接时,是同步字符链接本身还是其所指向的文件?即是否追踪字符链接文件?
  • 目标文件已存在时,是否做备份?
  • 等等其他情况。

上面这些操作,都是需要rsync来完成的,这也就是为什么在使用rsync的时候要求源和目标主机都需要安装有rsync程序包。

rsync的同步由检查模式和同步模式两部分构成。

检查模式

检查模式用于检查哪些文件应该被同步,哪些文件不应该被同步。比如--exclude选项排除了不应该被同步的文件。默认情况下,rsync使用quick check算法来检查源文件和目标文件的size(文件大小)与mtime。当size或者mtime不同的时候,就会判定文件应该被同步。

可以通过一些选项来修改检查模式,例如--size-only用于指明将仅检查文件的size而不检查mtime。

还有其他的选项也可以用于修改检查模式,弹性十足。

同步模式

同步模式用于处理上面所说的“是否删除本地主机上没有但是远程主机上多出来的文件?”之类的问题。同步模式也有许多选项可以指定,也是弹性十足。

一般情况下我们是修改同步模式,少数情况下才会修改检查模式,因为后者的修改容易引起性能的较大改动(例如--checksum)或者没有正确同步应该同步的文件(例如--size-only)。

三种工作方式

rsync有三种工作方式,即三种语法格式,如下。

Local:  rsync [OPTION...] SRC... [DEST]

Access via remote shell:
Pull: rsync [OPTION...] [USER@]HOST:SRC... [DEST]
Push: rsync [OPTION...] SRC... [USER@]HOST:DEST Access via rsync daemon:
Pull: rsync [OPTION...] [USER@]HOST::SRC... [DEST]
rsync [OPTION...] rsync://[USER@]HOST[:PORT]/SRC... [DEST]
Push: rsync [OPTION...] SRC... [USER@]HOST::DEST
rsync [OPTION...] SRC... rsync://[USER@]HOST[:PORT]/DEST
  1. 第一种方式是上述的Local部分,为本地主机内部的同步。
  2. 第二种方式是上述的remote shell部分,为不同主机通过远程shell实现同步。
  3. 第三种方式是上述的rsync daemon部分,为不同主机通过网络套接字实现同步。这种方式在指定daemon的时候有两种形式。有双冒号形式和URL形式,都是可以的,个人倾向于URL形式,比较不会和单冒号形式相似。前面演示的示例都是本地主机或者远程shell,最后再单独讲rsync daemon方式。

前两者的本质是基于管道;第三种是需要先在远程主机上运行一个daemon,监听在某个端口上,然后本地主机通过网络套接字与其通信。

还有一种特殊的方式,是基于远程shell连接上之后,在远程主机上临时运行一个daemon,当数据同步完毕后,daemon也会结束。这种方式的命令行语法类似rsync daemon模式,不同的点在于选项需要指明-e或者--rsh。

本文中我们将其称为“临时daemon”,注意,这只是个人对其称呼,非官方。

命令的参数可以有多个;当指定多个的时候,只有最后一个表示目标文件,其余都为源文件,目标文件若不存在则自动创建为目录,若存在则必须得是目录,否则报错。

[root@C7 ~]# file /tmp/.txt
/tmp/.txt: empty
[root@C7 ~]# rsync /etc/fstab /etc/inittab /tmp/.txt
ERROR: destination must be a directory when copying more than file
rsync error: errors selecting input/output files, dirs (code ) at main.c() [Receiver=3.1.]

如果参数只有一个SRC的话,那么等同于“ls -l”。

[root@c7-client ~]# rsync /etc/fstab
-rw-r--r-- // :: fstab
[root@c7-client ~]# rsync /etc/ssh/
drwxr-xr-x // :: .
-rw-r--r-- , // :: moduli
-rw-r--r-- , // :: ssh_config
-rw-r----- // :: ssh_host_ecdsa_key
-rw-r--r-- // :: ssh_host_ecdsa_key.pub
-rw-r----- // :: ssh_host_ed25519_key
-rw-r--r-- // :: ssh_host_ed25519_key.pub
-rw-r----- , // :: ssh_host_rsa_key
-rw-r--r-- // :: ssh_host_rsa_key.pub
-rw------- , // :: sshd_config
[root@c7-client ~]# rsync root@192.168.17.7:/etc/fstab
root@192.168.17.7's password:
-rw-r--r-- // :: fstab
[root@c7-client ~]# rsync root@192.168.17.7:/etc/ssh/
root@192.168.17.7'
s password:
drwxr-xr-x // :: .
-rw-r--r-- , // :: moduli
-rw-r--r-- , // :: ssh_config
-rw-r----- // :: ssh_host_ecdsa_key
-rw-r--r-- // :: ssh_host_ecdsa_key.pub
-rw-r----- // :: ssh_host_ed25519_key
-rw-r--r-- // :: ssh_host_ed25519_key.pub
-rw-r----- , // :: ssh_host_rsa_key
-rw-r--r-- // :: ssh_host_rsa_key.pub
-rw------- , // :: sshd_config

当使用远程shell的时候,会提示我们输入远程主机的用户密码。由于远程shell方式是基于SSH,因此可以事先配置好SSH的免密登录,这样就无需密码了。

[root@c7-client ~]# rsync root@192.168.17.7:/etc/fstab
-rw-r--r-- // :: fstab

本地主机文件和目录的复制,类似cp命令。

[root@c7-client ~]# rsync /etc/fstab /tmp/
[root@c7-client ~]# ls -l /etc/fstab /tmp/fstab
-rw-r--r--. root root Dec : /etc/fstab
-rw-r--r--. root root Dec : /tmp/fstab
[root@c7-client ~]# rsync -r /etc/ssh /tmp/
[root@c7-client ~]# ls /{etc,tmp}/ssh/
/etc/ssh/:
moduli ssh_config sshd_config ssh_host_ecdsa_key ssh_host_ecdsa_key.pub ssh_host_ed25519_key ssh_host_ed25519_key.pub ssh_host_rsa_key ssh_host_rsa_key.pub /tmp/ssh/:
moduli ssh_config sshd_config ssh_host_ecdsa_key ssh_host_ecdsa_key.pub ssh_host_ed25519_key ssh_host_ed25519_key.pub ssh_host_rsa_key ssh_host_rsa_key.pub

复制或者同步目录的时候,需要注意一点,如果源目录的末尾没有斜线,则表示将源目录整个复制到目标目录下,就如同上面的例子;

如果源目录的末尾有斜线,则表示将源目录下的内容复制到目标目录下,如下所示。

[root@c7-client ~]# rsync -r /etc/ssh/ testdir/
[root@c7-client ~]# ls /etc/ssh/ testdir/
/etc/ssh/:
moduli ssh_config sshd_config ssh_host_ecdsa_key ssh_host_ecdsa_key.pub ssh_host_ed25519_key ssh_host_ed25519_key.pub ssh_host_rsa_key ssh_host_rsa_key.pub testdir/:
moduli ssh_config sshd_config ssh_host_ecdsa_key ssh_host_ecdsa_key.pub ssh_host_ed25519_key ssh_host_ed25519_key.pub ssh_host_rsa_key ssh_host_rsa_key.pub

远程shell之间的文件同步。

[root@c7-client ~]# rsync c7-client.txt root@192.168.17.7:/root/
[root@c7-client ~]# rsync root@192.168.17.7:/root/c7.txt .

远程shell之间的目录同步。

[root@c7-client ~]# touch testdir/rsync{,,}.txt
[root@c7-client ~]# rsync -r testdir/ root@192.168.17.7:/root/
[root@C7 ~]# ls -l /root/rsync{,,}.txt
-rw-r--r-- root root Dec : /root/rsync1.txt
-rw-r--r-- root root Dec : /root/rsync2.txt
-rw-r--r-- root root Dec : /root/rsync3.txt
[root@c7-client ~]# rsync -r root@192.168.17.7:/root/rrr .
[root@c7-client ~]# ls -l rrr/
total
-rw-r--r--. root root Dec : l.txt
-rw-r--r--. root root Dec : w.txt
-rw-r--r--. root root Dec : z.txt

选项与示例

-v, --verbose:用于显示同步中的详细信息,-vvvv可以显示更详细的信息,一般是用于排错的时候使用。v的个数越多,显示的信息越详细。

--partial:默认情况下,当传输中断的时候,rsync会将已传输一半的目标文件删除;如果使用该选项,则不会删除,这样子可以提高再次传输的速度,感觉类似于断点重传。

--progress:在同步的时候显示进度,类似如下。

  %  .64kB/s    ::

-P:该选项是--partial和--progress的结合,当同步的时间较长(文件较大较多或者网络不佳)的时候,可以使用该选项。

-n, --dry-run:用于测试,不会真正执行同步操作,而是模拟真实操作,并且模拟真实的输出,常配合-v或者-vvvv来查看rsync是否执行正常。

-a, --archive:归档模式,等同于-rlptgoD,它保留了源文件的大部分元数据。但是它不包含-H选项,因为查找多链接的文件太消耗资源。

-r, --recursive:递归,用于同步目录。如果不加该选项,当源文件是一个目录的时候,会跳过,并返回被跳过的目录名称。

[root@C7 tmp]# rsync -v /foo/ /tmp/
skipping directory . sent bytes received bytes 56.00 bytes/sec
total size is speedup is 0.00
[root@C7 tmp]# rsync -v /foo /tmp/
skipping directory foo sent bytes received bytes 56.00 bytes/sec
total size is speedup is 0.00

这里从输出信息中也可以看出源文件如果是目录,那么末尾是否带斜线的差异。

[root@C7 tmp]# rsync -rv /foo/ /tmp/
sending incremental file list
bar/baz.c sent bytes received bytes 334.00 bytes/sec
total size is speedup is 0.00

-t, --times:保留文件的mtime,由于rsync默认检查模式使用quick check算法,因此建议每次同步都应该使用-t或者-a选项,否则发送端每次都会将很可能是相同的文件加入文件列表,导致了系统资源的浪费。

-o, --owner:保留文件的属主。

-g, --group:保留文件的属组。

--chown=USER:GROUP:文件同步后修改目标文件的ownership。该选项其实等同于“--usermap=*:USER --groupmap=*:GROUP”。不过该选项的实现是内部调用了--usermap和--groupmap,因此该选项不可以与这两个选项混合使用。如果想要让--chown正常使用,不可缺少-o和-g选项。

-p, --perms:保留文件的读写执行权限,不包含其他特殊权限。

--chmod:文件同步后修改目标文件的权限。可根据文件是普通文件或者目录分别设置不同的权限。如果是普通文件则会加上前缀F,目录则会加上前缀D。用法类似如下。

--chmod=Dg+s,ug+w,Fo-w,+X
--chmod=D2775,F664

如果想要让--chown正常使用,不可缺少-o和-g选项。

因此,如果想要让--chown和--chmod都正常使用的话,使用-a选项即可!但是具体的原因目前未知,应该是rsync对待所有权(ownership)和权限(permission)的理解与我们人脑不同。

让我们来验证一下-togp选项的作用,在c7-client主机上创建了一个空文件rsync.txt,并设置了权限和ownership,然后同步至C7主机。可以发现同步后,文件的权限、所有权以及mtime都发生了改变。

[root@c7-client ~]# ls -l rsync.txt
-rw-rw-rw-. haimianbb haimianbb Dec : rsync.txt
[root@c7-client ~]# rsync -v rsync.txt root@192.168.17.7:/tmp/rsync.txt
rsync.txt sent bytes received bytes 236.00 bytes/sec
total size is speedup is 0.00
[root@C7 ~]# ls -l /tmp/rsync.txt
-rw-r--r-- root root Dec : /tmp/rsync.txt

当我们使用了-togp选项之后,文件的权限、mtime和所有权都保持了。

[root@c7-client ~]# rsync -vtogp rsync.txt root@192.168.17.7:/tmp/rsync.txt
rsync.txt sent bytes received bytes 296.00 bytes/sec
total size is speedup is 0.00
[root@C7 ~]# ls -l /tmp/rsync.txt
-rw-rw-rw- haimianbb haimianbb Dec : /tmp/rsync.txt

-D:等同于--devices和--specials的结合,用于同步字符设备、块设备以及特殊文件(socket)。

-l, --links:当遇到字符链接的时候,在远程主机上创建相同的字符链接文件。

默认情况下,字符链接文件是非普通文件,会被rsync跳过。

[root@C7 tmp]# cat /tmp/name.txt
zhangwenlong
[root@C7 tmp]# ln -s /tmp/name.txt /tmp/name.link
[root@C7 tmp]# ls -l name.link
lrwxrwxrwx root root Dec : name.link -> /tmp/name.txt
[root@C7 tmp]# rsync -v name.link /root/name.rsync
skipping non-regular file "name.link" sent bytes received bytes 194.00 bytes/sec
total size is speedup is 0.13

使用-l选项就可以同步字符链接文件了,同步后的文件也是一个字符链接文件。

[root@C7 tmp]# rsync -vl name.link /root/name.rsync
name.link -> /tmp/name.txt sent bytes received bytes 158.00 bytes/sec
total size is speedup is 0.16
[root@C7 tmp]# ls -l /root/name.rsync
lrwxrwxrwx root root Dec : /root/name.rsync -> /tmp/name.txt
[root@C7 tmp]# cat /root/name.rsync
zhangwenlong

-z, --compress:传输之前是否对数据进行压缩。可以留意到压缩后,发送的字节数降低了。

[root@C7 tmp]# rsync -v /etc/inittab /tmp/inittab
inittab sent bytes received bytes ,250.00 bytes/sec
total size is speedup is 0.82
[root@C7 tmp]# rsync -vz /etc/inittab /tmp/inittab
inittab sent bytes received bytes 798.00 bytes/sec
total size is speedup is 1.28

-R, --relative:使用相对路径。意味着会将命令行中完整的路径名称发送给远程主机,而不是只发送最后一部分的文件名。

我们先来看一下默认情况下的行为。复制/foo/bar/baz.c到/tmp/目录下的时候,仅会在/tmp/目录下创建baz.c文件。(从-v结果来看,应理解为仅将源文件“/foo/bar/baz.c”中的最后一部分“baz.c”加入了文件列表)

[root@C7 ~]# rsync -v /foo/bar/baz.c /tmp/
baz.c sent bytes received bytes 226.00 bytes/sec
total size is speedup is 0.00

如果增加了-R选项。则是在/tmp/目录下创建了完整的目录结构。这是因为-R选项使得发送端rsync将整个源文件路径“/foo/bar/baz.c”加入了文件列表。

[root@C7 ~]# rsync -vR /foo/bar/baz.c /tmp/
/foo/
/foo/bar/
/foo/bar/baz.c sent bytes received bytes 338.00 bytes/sec
total size is speedup is 0.00
[root@C7 ~]# tree /tmp/foo/
/tmp/foo/
└── bar
└── baz.c directory, file

像“foo/”和“foo/bar/”这种额外的路径元素,我们就称其为隐式目录(implied  directories)。如果相对路径涉及到字符链接的话,可能会有特殊情况,要看一下man手册。

有的时候你可能只希望在接收端创建部分源文件路径,那么可以在源文件路径中插入一个“.”和“/”。

[root@C7 tmp]# rsync -vR /foo/./bar/baz.c /tmp/
bar/
bar/baz.c sent bytes received bytes 276.00 bytes/sec
total size is speedup is 0.00

这样子相对路径就是从bar开始的了。

--size-only:修改rsync默认的quick check算法,仅检查两端文件大小。个人建议不要带上该选项,虽然检查的复杂度降低了,可能会提升性能,但是可能出现内容不同的文件没得到正确的同步,在特定的环境下可能造成严重的后果。

就像这个例子中所示,1.txt和2.txt明明文件内容不一致,却因为--size-only选项使得它们没有被同步。

[root@C7 tmp]# ls -l {,}.txt
-rw-r--r-- root root Dec : .txt
-rw-r--r-- root root Dec : .txt
[root@C7 tmp]# diff {,}.txt
1c1
<
---
>
[root@C7 tmp]# rsync -v --size-only {,}.txt sent bytes received bytes 102.00 bytes/sec
total size is speedup is 0.18
[root@C7 tmp]# diff {,}.txt
1c1
<
---
>

-c, --checksum:修改rsync默认的quick check算法,通过检验码的方式检查两端的文件内容是否一致,生成校验码会引起两端产生大量的磁盘I/O,因此一般是不会启用该选项

-u, --update:如果目标文件存在且mtime新于源文件的话,则跳过。如果目标文件存在且mtime等于源文件的话,则判断size,size同则不同步,size不同则同步。该选项是传输规则,并不是exclude,因此不会影响文件进入文件列表,因此不会影响目标文件上的删除操作。

-d, --dirs:用于拷贝目录本身,但是不会拷贝目录下的文件。有别于-r, --recursive。但是如果源文件是以“.”或者斜杠结尾的话,那么还是会将目录下的内容拷贝至目标文件。如果-r和-d同时使用的话,那么-r优先。

--max-size=SIZE:限制传输的文件的最大size。可以带上单位后缀“K、KiB、M、MiB、G、GiB”,这是以1024为乘数,如果想要以1000为乘数的话,则使用“KB、MB和GB”。大小写均可。

--min-size=SIZE:限制传输的文件的最小size。其他说明类似上面的--max-size,避免传输较小的文件或者垃圾文件。

--exclude=PATTERN:用于排除文件,被排除的文件不会被同步。涉及到man手册中的过滤规则(FILTER RULE)。后面会详述。

--exclude-from=FILE:排除文件,即当需要排除的PATTERN较多的时候,可以将每一个PATTERN都写入该文件中,每个PATTERN占一行。空行以及以“#”或者“;”开头的行会被忽略。FILE为“-”表示读取STDIN(标准输入)。

--delete:删除接收端中不存在于发送端的额外的文件,仅作用于那些已经同步了的目录。后面会详述。

-b, --backup:当目标文件已经存在的时候,同步操作即将覆盖或者同步操作(--delete)即将删除目标文件的时候,对其进行备份。

[root@C7 tmp]# rsync -vb /foo/bar/baz.c /tmp/
baz.c sent bytes received bytes 226.00 bytes/sec
total size is speedup is 0.00
[root@C7 tmp]# ls -l /tmp/baz.c*
-rw-r--r-- root root Dec : /tmp/baz.c
-rw-r--r-- root root Dec : /tmp/baz.c~

即便反复执行,备份文件的文件名依然是“baz.c~”。

--backup-dir=DIR:指定备份文件的目录。默认的备份目录是在目标文件的当前目录。

若备份目录不存在,则会自动创建,并回显。

[root@C7 tmp]# rsync -vb --backup-dir=/tmp/back/ /foo/bar/baz.c /tmp/
(new) backup_dir is /tmp/back
baz.c sent bytes received bytes 294.00 bytes/sec
total size is speedup is 0.00

备份文件会是/tmp/back/baz.c,它不带后缀,反复执行也依然是该文件。

--suffix=SUFFIX:指定备份文件的后缀。默认的备份后缀是“~”。

[root@C7 tmp]# rsync -vb --suffix=alongdidi /foo/bar/baz.c /tmp/
baz.c sent bytes received bytes 226.00 bytes/sec
total size is speedup is 0.00

备份文件是/tmp/baz.calongdidi,反复执行的话也依然是该文件。

rsync的备份机制似乎不能递增,只能反复覆盖,感觉并没有什么卵用。

-e, --rsh=COMMAND:用于选择一个远程shell程序,现在所使用的一般都是SSH了(以前是RSH,但是RSH没有加密功能),所以一般是用于指定SSH的选项。

主要用于指定ssh的参数,以及用于临时daemon。

-e 'ssh -p 2234'
-e 'ssh -o "ProxyCommand nohup ssh firewall nc -w1 %h %p"'

--port=PORT:如果使用的rsync daemon的工作方式的时候,用于指定端口号,默认是873。

--password-file=FILE:用于指定rsync认证的密码文件。非SSH认证的密码。FILE可以为“-”表示读取STDIN。如果密码文件是全局可读,或者以root用户运行rsync但是密码文件的ownership不是root的话,那么rsync会退出。

--existing, --ignore-non-existing:告诉rsync,只更新目标文件中已存在的文件。

--ignore-existing:告诉rsync,只更新目标文件中不存在的文件。

创建了2个目录,目录结构如下。默认情况下,将a目录同步至b目录的话,除了会将a.txt加入文件列表中以外,还会将{1,2,3}.txt也加入文件列表中。

[root@C7 tmp]# tree {a,b}
a
├── .txt
├── .txt
├── .txt
└── a.txt
b
├── .txt
├── .txt
├── .txt
└── b.txt directories, files

如果我们只想同步接收端已存在的文件的话,则使用--existing选项。

[root@C7 tmp]# rsync -rv --existing a/ b/
sending incremental file list
.txt
.txt
.txt sent bytes received bytes 616.00 bytes/sec
total size is speedup is 0.00

如果我们只想同步接收端不存在的文件的话,则使用--ignore-existing选项。

[root@C7 tmp]# rsync -rv --ignore-existing a/ b/
sending incremental file list
a.txt sent bytes received bytes 384.00 bytes/sec
total size is speedup is 0.00

--existing选项可以和--ignore-existing选项结合使用,结合使用的情况下不会同步任何文件,一般用于配合--delete选项实现在不传输任何数据的情况下,仅删除接收端的额外文件。

可以看一下单独使用--delete和结合使用的区别。

[root@C7 tmp]# rsync -rv --delete a/ b/
sending incremental file list
deleting b.txt
.txt
.txt
.txt
a.txt sent bytes received bytes 750.00 bytes/sec
total size is speedup is 0.00 [root@C7 tmp]# rsync -rv --existing --ignore-existing --delete a/ b/
sending incremental file list
deleting b.txt sent bytes received bytes 270.00 bytes/sec
total size is speedup is 0.00

--remove-source-files:源文件如果同步成功,则将其删除。

[root@C7 tmp]# tree {a,b}
a
├── .txt
├── .txt
├── .txt
└── a.txt
b
├── .txt
├── .txt
├── .txt
└── b.txt directories, files
[root@C7 tmp]# rsync -rv --remove-source-files a/ b/
sending incremental file list
.txt
.txt
.txt
a.txt sent bytes received bytes 796.00 bytes/sec
total size is speedup is 0.00
[root@C7 tmp]# tree {a,b}
a
b
├── .txt
├── .txt
├── .txt
├── a.txt
└── b.txt directories, files

rsync的include/exclude模式

--exclude=PATTERN:rsync通过该选项来排除所不需要同步的文件。它是--filter选项的一种简写形式。

一个--exclude选项只能排除一个模式,如果要排除多个模式的话,可以在命令行中书写多次--exclude选项,或者将模式逐行写入文件中,然后使用--exclude-from引用该模式文件。

简单的示例如下。

[root@C7 tmp]# tree {a,b}
a
├── .txt
├── .txt
├── .txt
└── a.txt
b
├── .txt
├── .txt
└── .txt directories, files
[root@C7 tmp]# rsync -rv --exclude=a.txt a/ b/
sending incremental file list
.txt
.txt
.txt sent bytes received bytes 576.00 bytes/sec
total size is speedup is 0.00
[root@C7 tmp]# rsync -rv --exclude=a.txt a b/
sending incremental file list
a/
a/.txt
a/.txt
a/.txt sent bytes received bytes 614.00 bytes/sec
total size is speedup is 0.00

第二次同步的时候,如果不加--exclude选项,那么会同步以下:

  • a/
  • a/1.txt
  • a/2.txt
  • a/3.txt
  • a/a.txt

我们排除的模式是a.txt,模式没有以“/”开头或者结尾,那么只要传输整个名称中某个部分包含了a.txt,就会生效。因此,a/a.txt也被排除掉了。

排除的时候,模式的书写其实是一个难点。在man手册中有比较详细的描述,涉及到

  • 源文件如果是个目录,那么末尾是否带上斜线。
  • 是否使用相对目录的选项--relative。
  • 什么是传输中的根目录。
  • 模式是否以“/”开头。若是则涉及到锚定传输根的概念。
  • 模式是否以“/”结尾。若是则判定为目录。
  • 模式是否是无限制(unqualified)的,例如“foo”或者上述的“a.txt”。无限制指的是没有以“/”开头或者结尾。若是则匹配的灵活度是很大的。
  • 等等。

这个需要大家自行看一下man手册以及自己反复敲命令体验一下,一般是带上-avn选项来测试。

再来几个示例,首先我们先看一下目录a的层级结构。

[root@C7 tmp]# tree a/
a/
├── .txt
├── .txt
├── .txt
├── a.txt
├── index.php
└── public
└── index.php directory, files

然后我们以测试的形式,同步a目录至b目录,注意此时同步的源目录a的末尾带上了斜线。首先我们看一下不带排除选项的话,会同步哪些文件。

[root@C7 tmp]# rsync -avn a/ b/
sending incremental file list
./
.txt
.txt
.txt
a.txt
index.php
public/
public/index.php sent bytes received bytes 546.00 bytes/sec
total size is speedup is 0.00 (DRY RUN)

源目录带上斜线,并且没有-R选项,这时候就已经确定了传输根了。即这个示例中的传输根是a/下开始。而不是a目录本身开始,更不是/tmp/a(案例中我的PWD目录是/tmp)开始。

这时候我们排除无限制的index.php。

[root@C7 tmp]# rsync -avn --exclude=index.php a/ b/
sending incremental file list
./
.txt
.txt
.txt
a.txt
public/ sent bytes received bytes 420.00 bytes/sec
total size is speedup is 0.00 (DRY RUN)

无限制的匹配范围很大,只要整棵树/整个名称中包含了模式,就会生效。因此index.php和public/index.php都会排除掉。

接下来我们来一个有限制的,排除掉传输根下的index.php。

[root@C7 tmp]# rsync -avn --exclude=/index.php a/ b/
sending incremental file list
./
.txt
.txt
.txt
a.txt
public/
public/index.php

排除模式不改变,通过修改源目录末尾的斜线,从而改变了传输根,再来看看变化。

[root@C7 tmp]# rsync -avn --exclude=/index.php a b/
sending incremental file list
a/
a/.txt
a/.txt
a/.txt
a/a.txt
a/index.php
a/public/
a/public/index.php sent bytes received bytes 572.00 bytes/sec
total size is speedup is 0.00 (DRY RUN)

这个时候想要再排除a目录下的index.php,就得输入--exclude=a/index.php或者--exclude=/a/index.php了。

如果模式末尾带上斜线,则表示这个模式匹配到的,一定得是一个目录。因为index.php不是目录,所以不会被排除了。

[root@C7 tmp]# rsync -avn --exclude=index.php/ a/ b/
sending incremental file list
./
.txt
.txt
.txt
a.txt
index.php
public/
public/index.php sent bytes received bytes 546.00 bytes/sec
total size is speedup is 0.00 (DRY RUN)

--exclude用于排除,而--include用于包含。当同一个文件,既被排除又被包含的时候,要看哪个选项放在前面。先遇到先生效。

[root@C7 tmp]# rsync -avn --exclude=index.php --include=index.php a/ b/
sending incremental file list
./
.txt
.txt
.txt
a.txt
public/
public/test.txt sent bytes received bytes 478.00 bytes/sec
total size is speedup is 0.00 (DRY RUN)
[root@C7 tmp]# rsync -avn --include=index.php --exclude=index.php a/ b/
sending incremental file list
./
.txt
.txt
.txt
a.txt
index.php
public/
public/index.php
public/test.txt sent bytes received bytes 610.00 bytes/sec
total size is speedup is 0.00 (DRY RUN)

当include和exclude规则同时使用且使用了递归选项(-r或者-a)的时候,如果排除了某个文件的父目录(或者爷爷目录等更高层的目录),那么即便该文件被包含了并且include的规则放在最前面,该文件也仍然不会被同步。

[root@C7 tmp]# rsync -avn --include=public/index.php --exclude=public/ a/ b/
sending incremental file list
./
.txt
.txt
.txt
a.txt
index.php sent bytes received bytes 392.00 bytes/sec
total size is speedup is 0.00 (DRY RUN)

rsync的--delete选项

选项与示例中我们已经解释了--delete选项的作用,在上文--existing和--ignore-existing结合使用的示例中我们也初次使用了它。

根据工作方式,即可删除本地主机上的文件,也可以删除目标主机上的文件。如果以一个空目录作为源文件的话,则会清空目标目录。

当--delete选项和--exclude选项同时使用的时候,被排除的文件不会被删除。我们来验证一下。

文件层级结构如下。

[root@C7 tmp]# tree test{,}
test1
├── .txt
├── .txt
└── .txt
test2
├── .txt
├── .txt
├── .txt
├── apple.jpg
├── icon.jpg
├── my.cnf
├── mysqld.log
└── pear.jpg directories, files

仅删除不排除的情况。

[root@C7 tmp]# rsync -rvn --delete test1/ test2/
sending incremental file list
deleting pear.jpg
deleting mysqld.log
deleting my.cnf
deleting icon.jpg
deleting apple.jpg
.txt
.txt
.txt sent bytes received bytes 362.00 bytes/sec
total size is speedup is 0.00 (DRY RUN)

增加排除的情况。可见被排除的模式,就不再会被删除了。

[root@C7 tmp]# rsync -rvn --delete --exclude=*.jpg test1/ test2/
sending incremental file list
deleting mysqld.log
deleting my.cnf
.txt
.txt
.txt sent bytes received bytes 288.00 bytes/sec
total size is speedup is 0.00 (DRY RUN)

验证无误,接下来我们来理解一下这是为什么。

rsync同步的过程中,发送端将需要传输的文件放入文件列表中(文件列表中的每一行我们称为条目),该文件列表会被传输给接收端。接收端的generator进程对每个条目计算数据块校验码(一个文件可以由多个数据块组成)并返回给发送端,发送端根据数据块校验码来判断哪些数据块应该被传输,这样就实现了rsync的增量传输功能——仅传输文件中不同的数据块,而非整个文件。

--exclude本质是一种筛选规则,如果某个文件被其指定了,那么该文件在加入文件列表的时候,会被标记为隐藏(hide)。

--delete删除的时间点是generator进程处理文件列表时、生成数据块校验码之前,这样子的好处是被--delete所删除的文件就不需要再去计算数据块校验码了。

小结一下:--exclude是在发送端创建文件列表的时候起作用;--delete是在文件列表已经生成并发送往接收端,在接收端才起作用。因此--exclude先于--delete。此时我们可能会认为,如果某个条目带有hide标记,则接收端会认为其不存在于发送端而将其删除,但是rsync却不是这么做。rsync还会将被--exclude的文件标记为保护(protect),防止其结合--delete使用的时候被误删除,如果确实要删除的话,可以使用--delete-excluded。

结语

本文目前只涉及到rsync的本地和远程shell的使用。daemon和临时daemon暂时没有涉及,如果将来在工作中有使用到的话可能会补上。已经在rsync上使用了较多的时间,暂时先告一段落,再次感谢博主:骏马金龙。

工作中主要是使用了rsync作为PHP代码部署的工具。结合排除功能与shell脚本,应该是可以满足一般的小企业了。

rsync基础的更多相关文章

  1. rsync的man手册(未完成)

    本文是man rsync的官方手册译文,版本是3.1.2. 本文没打算翻译每个option,常用的option已经在另一篇文章rsync基础中有描述. 一开始的翻译过程比较顺畅,越到后面越难以理解,侧 ...

  2. rsync+inotify同步备份文件

    前言 rsync作用:man rsync可以看到解释为a fast, versatile, remote (and local) file-copying tool,主要进行文件的同步. inotif ...

  3. Linux基础命令介绍七:网络传输与安全 wget curl rsync iptables

    本篇接着介绍网络相关命令:wget 文件下载工具.curl 网络数据传输工具.rsync 文件传输工具等. 本篇接着介绍网络相关命令 1.wget 文件下载工具 wget [option]... [U ...

  4. Linux基础学习-数据备份工具Rsync

    数据备份工具rsync 作为一个系统管理员,数据备份是非常重要的,如果没有做好备份策略,磁盘损坏了,那么你的数据将全部丢失,所以在日常的维护工作中,一定要时刻牢记给数据做备份. rsync不仅可以可以 ...

  5. 【基础】:Rsync数据同步工具

    第二十一节 Rsync数据同步工具 1.1 Rsync介绍 1.1.1 什么是Rsync? 1.1.2 Rsync简介 1.3 Rsync的特性 1.1.4 Rsync的企业工作场景说明 1.2 Rs ...

  6. Git基础操作

    配置秘钥 1.检查本机有没有秘钥 检查~/.ssh看看是否有名为d_rsa.pub和id_dsa.pub的2个文件. $ ~/.sshbash: /c/Users/lenovo/.ssh: Is a ...

  7. Linux基础精华

    Linux基础精华 (继续跟新中...) 常用命令: Linux shell 环境 让你提升命令行效 率的 Bash 快捷键 [完整版] 设置你自己的liux alias Linux的Find使用 L ...

  8. centos rsync安装配置

    安装 1 yum -y install rsync ---------------------服务器安装------------------------------- 创建基础配置文件 1 2 3 4 ...

  9. linux入门基础_centos(一)--基础命令和概念

    闲来无事干,看看2014自己整理的一些学习笔记.独乐了不如众乐乐吗! 贴出来和大家分享一下,由于篇幅比较长,分成几篇发布吧,由于是学习笔记,可能有些地方写的不是很正确或者说不详细,或者你会看到上面的课 ...

随机推荐

  1. 「Vue」父子组件之间的传值及调用方法

    a.父组件向子组件传值data(){},props数据区别data中的数据可读可写,是自己的数据props是个数组,中的数据是父组件传递过来的,只读不能写<login :dmsg='msg'&g ...

  2. [整理]JS中的状态机

    /*StateMachine*/ var StateMachine = (function(){ function StateMachine(opts){ this.current = opts.in ...

  3. 被误解的 Node.js

    http://www.ibm.com/developerworks/cn/web/1201_wangqf_nodejs/ 被误解的 Node.js

  4. es6笔记(4) Set数据结构

    概要 介绍: 集合是由一组无序且唯一的项组成的,这个数据结构使用了与有限集合相同的数学概念,应用在计算机的数据结构中. ES6提供了数据结构Set.它类似于数组,但是没有重复的值. 特点: key与v ...

  5. 01:MFC应用程序编程

    一 MFC的发展 VC 1.0->VC 5.0->VC 6.0->VC2008 SP1)->VS2010 二 MFC基础 1 MFC 微软基础类库 采用类的方式,将Win32 ...

  6. ajax函数说明

    url: 要求为String类型的参数,(默认为当前页地址)发送请求的地址. type: 要求为String类型的参数,请求方式(post或get)默认为get.注意其他http请求方法,例如put和 ...

  7. Java内存模型-final域的内存语义

    一 引言 说到final你肯定知道它是Java中的关键字,那么它所在Java中的作用你知道吗?不知道的话,请前往这篇了解下https://www.cnblogs.com/yuanfy008/p/802 ...

  8. blog迁移

    blog迁移到: https://github.com/for-firecat/

  9. 标准linu休眠和唤醒机制分析(四)【转】

    转自:http://blog.csdn.net/lizhiguo0532/article/details/6453552 suspend第三.四.五阶段:platform.processor.core ...

  10. df -h执行卡住不动问题解决【转】

    昨天生产环境报日志写不进去了,因此 登陆线上环境后,习惯用df -h命令查看空间使用情况,结果发现该命令执行半天也没有返回. 因此使用mount命令查看该机器上的目录: [conversant@swi ...