rsync基础

参考资料:骏马金龙的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/1.txt 
/tmp/1.txt: empty
[root@C7 ~]# rsync /etc/fstab /etc/inittab /tmp/1.txt
ERROR: destination must be a directory when copying more than 1 file
rsync error: errors selecting input/output files, dirs (code 3) at main.c(623) [Receiver=3.1.2]

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

[root@c7-client ~]# rsync /etc/fstab 
-rw-r--r--            487 2018/12/18 11:46:20 fstab
[root@c7-client ~]# rsync /etc/ssh/
drwxr-xr-x            225 2018/12/18 12:04:37 .
-rw-r--r--        581,843 2018/04/11 12:21:29 moduli
-rw-r--r--          2,276 2018/04/11 12:21:29 ssh_config
-rw-r-----            227 2018/12/18 12:04:37 ssh_host_ecdsa_key
-rw-r--r--            162 2018/12/18 12:04:37 ssh_host_ecdsa_key.pub
-rw-r-----            387 2018/12/18 12:04:37 ssh_host_ed25519_key
-rw-r--r--             82 2018/12/18 12:04:37 ssh_host_ed25519_key.pub
-rw-r-----          1,679 2018/12/18 12:04:35 ssh_host_rsa_key
-rw-r--r--            382 2018/12/18 12:04:35 ssh_host_rsa_key.pub
-rw-------          3,907 2018/04/11 12:21:29 sshd_config
[root@c7-client ~]# rsync root@192.168.17.7:/etc/fstab
root@192.168.17.7's password: 
-rw-r--r--            465 2018/09/27 15:49:45 fstab
[root@c7-client ~]# rsync root@192.168.17.7:/etc/ssh/
root@192.168.17.7's password: 
drwxr-xr-x            225 2018/09/27 17:28:23 .
-rw-r--r--        581,843 2018/04/11 12:21:29 moduli
-rw-r--r--          2,276 2018/04/11 12:21:29 ssh_config
-rw-r-----            227 2018/09/27 16:02:33 ssh_host_ecdsa_key
-rw-r--r--            162 2018/09/27 16:02:33 ssh_host_ecdsa_key.pub
-rw-r-----            387 2018/09/27 16:02:33 ssh_host_ed25519_key
-rw-r--r--             82 2018/09/27 16:02:33 ssh_host_ed25519_key.pub
-rw-r-----          1,679 2018/09/27 16:02:33 ssh_host_rsa_key
-rw-r--r--            382 2018/09/27 16:02:33 ssh_host_rsa_key.pub
-rw-------          3,905 2018/09/27 17:28:23 sshd_config

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

[root@c7-client ~]# rsync root@192.168.17.7:/etc/fstab
-rw-r--r--            465 2018/09/27 15:49:45 fstab

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

[root@c7-client ~]# rsync /etc/fstab /tmp/
[root@c7-client ~]# ls -l /etc/fstab /tmp/fstab 
-rw-r--r--. 1 root root 487 Dec 18 11:46 /etc/fstab
-rw-r--r--. 1 root root 487 Dec 19 19:04 /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{1,2,3}.txt
[root@c7-client ~]# rsync -r testdir/ root@192.168.17.7:/root/
[root@C7 ~]# ls -l /root/rsync{1,2,3}.txt
-rw-r--r-- 1 root root 0 Dec 19 19:17 /root/rsync1.txt
-rw-r--r-- 1 root root 0 Dec 19 19:17 /root/rsync2.txt
-rw-r--r-- 1 root root 0 Dec 19 19:17 /root/rsync3.txt
[root@c7-client ~]# rsync -r root@192.168.17.7:/root/rrr .
[root@c7-client ~]# ls -l rrr/
total 0
-rw-r--r--. 1 root root 0 Dec 19 19:19 l.txt
-rw-r--r--. 1 root root 0 Dec 19 19:19 w.txt
-rw-r--r--. 1 root root 0 Dec 19 19:19 z.txt

 

选项与示例

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

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

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

782448  63%  110.64kB/s    0:00:04

-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 16 bytes  received 12 bytes  56.00 bytes/sec
total size is 0  speedup is 0.00
[root@C7 tmp]# rsync -v /foo /tmp/
skipping directory foo

sent 16 bytes  received 12 bytes  56.00 bytes/sec
total size is 0  speedup is 0.00

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

[root@C7 tmp]# rsync -rv /foo/ /tmp/
sending incremental file list
bar/baz.c

sent 131 bytes  received 36 bytes  334.00 bytes/sec
total size is 0  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-. 1 haimianbb haimianbb 0 Dec 25 10:06 rsync.txt
[root@c7-client ~]# rsync -v rsync.txt root@192.168.17.7:/tmp/rsync.txt
rsync.txt

sent 83 bytes  received 35 bytes  236.00 bytes/sec
total size is 0  speedup is 0.00
[root@C7 ~]# ls -l /tmp/rsync.txt 
-rw-r--r-- 1 root root 0 Dec 25 10:12 /tmp/rsync.txt

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

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

sent 113 bytes  received 35 bytes  296.00 bytes/sec
total size is 0  speedup is 0.00
[root@C7 ~]# ls -l /tmp/rsync.txt
-rw-rw-rw- 1 haimianbb haimianbb 0 Dec 25 10:06 /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 1 root root 13 Dec 25 17:32 name.link -> /tmp/name.txt
[root@C7 tmp]# rsync -v name.link /root/name.rsync
skipping non-regular file "name.link"

sent 43 bytes  received 54 bytes  194.00 bytes/sec
total size is 13  speedup is 0.13

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

[root@C7 tmp]# rsync -vl name.link /root/name.rsync
name.link -> /tmp/name.txt

sent 60 bytes  received 19 bytes  158.00 bytes/sec
total size is 13  speedup is 0.16
[root@C7 tmp]# ls -l /root/name.rsync 
lrwxrwxrwx 1 root root 13 Dec 25 17:35 /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 590 bytes  received 35 bytes  1,250.00 bytes/sec
total size is 511  speedup is 0.82
[root@C7 tmp]# rsync -vz /etc/inittab /tmp/inittab
inittab

sent 364 bytes  received 35 bytes  798.00 bytes/sec
total size is 511  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 78 bytes  received 35 bytes  226.00 bytes/sec
total size is 0  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 128 bytes  received 41 bytes  338.00 bytes/sec
total size is 0  speedup is 0.00
[root@C7 ~]# tree /tmp/foo/
/tmp/foo/
└── bar
    └── baz.c

1 directory, 1 file

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

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

[root@C7 tmp]# rsync -vR /foo/./bar/baz.c /tmp/
bar/
bar/baz.c

sent 100 bytes  received 38 bytes  276.00 bytes/sec
total size is 0  speedup is 0.00

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

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

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

[root@C7 tmp]# ls -l {1,2}.txt
-rw-r--r-- 1 root root 9 Dec 21 17:21 1.txt
-rw-r--r-- 1 root root 9 Dec 21 17:22 2.txt
[root@C7 tmp]# diff {1,2}.txt
1c1
< 12345678
---
> 87654321
[root@C7 tmp]# rsync -v --size-only {1,2}.txt

sent 39 bytes  received 12 bytes  102.00 bytes/sec
total size is 9  speedup is 0.18
[root@C7 tmp]# diff {1,2}.txt
1c1
< 12345678
---
> 87654321

-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 78 bytes  received 35 bytes  226.00 bytes/sec
total size is 0  speedup is 0.00
[root@C7 tmp]# ls -l /tmp/baz.c*
-rw-r--r-- 1 root root 0 Dec 21 17:28 /tmp/baz.c
-rw-r--r-- 1 root root 0 Dec 21 17:28 /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 78 bytes  received 69 bytes  294.00 bytes/sec
total size is 0  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 78 bytes  received 35 bytes  226.00 bytes/sec
total size is 0  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
├── 1.txt
├── 2.txt
├── 3.txt
└── a.txt
b
├── 1.txt
├── 2.txt
├── 3.txt
└── b.txt

0 directories, 8 files

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

[root@C7 tmp]# rsync -rv --existing a/ b/
sending incremental file list
1.txt
2.txt
3.txt

sent 235 bytes  received 73 bytes  616.00 bytes/sec
total size is 0  speedup is 0.00

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

[root@C7 tmp]# rsync -rv --ignore-existing a/ b/
sending incremental file list
a.txt

sent 157 bytes  received 35 bytes  384.00 bytes/sec
total size is 0  speedup is 0.00

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

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

[root@C7 tmp]# rsync -rv --delete a/ b/
sending incremental file list
deleting b.txt
1.txt
2.txt
3.txt
a.txt

sent 274 bytes  received 101 bytes  750.00 bytes/sec
total size is 0  speedup is 0.00


[root@C7 tmp]# rsync -rv --existing --ignore-existing --delete a/ b/
sending incremental file list
deleting b.txt

sent 114 bytes  received 21 bytes  270.00 bytes/sec
total size is 0  speedup is 0.00

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

[root@C7 tmp]# tree {a,b}
a
├── 1.txt
├── 2.txt
├── 3.txt
└── a.txt
b
├── 1.txt
├── 2.txt
├── 3.txt
└── b.txt

0 directories, 8 files
[root@C7 tmp]# rsync -rv --remove-source-files a/ b/
sending incremental file list
1.txt
2.txt
3.txt
a.txt

sent 274 bytes  received 124 bytes  796.00 bytes/sec
total size is 0  speedup is 0.00
[root@C7 tmp]# tree {a,b}
a
b
├── 1.txt
├── 2.txt
├── 3.txt
├── a.txt
└── b.txt

0 directories, 5 files

 

rsync的include/exclude模式

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

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

简单的示例如下。

[root@C7 tmp]# tree {a,b}
a
├── 1.txt
├── 2.txt
├── 3.txt
└── a.txt
b
├── 1.txt
├── 2.txt
└── 3.txt

0 directories, 7 files
[root@C7 tmp]# rsync -rv --exclude=a.txt a/ b/
sending incremental file list
1.txt
2.txt
3.txt

sent 215 bytes  received 73 bytes  576.00 bytes/sec
total size is 0  speedup is 0.00
[root@C7 tmp]# rsync -rv --exclude=a.txt a b/
sending incremental file list
a/
a/1.txt
a/2.txt
a/3.txt

sent 230 bytes  received 77 bytes  614.00 bytes/sec
total size is 0  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/
├── 1.txt
├── 2.txt
├── 3.txt
├── a.txt
├── index.php
└── public
    └── index.php

1 directory, 6 files

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

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

sent 232 bytes  received 41 bytes  546.00 bytes/sec
total size is 0  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
./
1.txt
2.txt
3.txt
a.txt
public/

sent 175 bytes  received 35 bytes  420.00 bytes/sec
total size is 0  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
./
1.txt
2.txt
3.txt
a.txt
public/
public/index.php

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

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

sent 244 bytes  received 42 bytes  572.00 bytes/sec
total size is 0  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
./
1.txt
2.txt
3.txt
a.txt
index.php
public/
public/index.php

sent 232 bytes  received 41 bytes  546.00 bytes/sec
total size is 0  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
./
1.txt
2.txt
3.txt
a.txt
public/
public/test.txt

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

sent 261 bytes  received 44 bytes  610.00 bytes/sec
total size is 0  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
./
1.txt
2.txt
3.txt
a.txt
index.php

sent 162 bytes  received 34 bytes  392.00 bytes/sec
total size is 0  speedup is 0.00 (DRY RUN)

 

rsync的--delete选项

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

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

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

文件层级结构如下。

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

0 directories, 11 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
1.txt
2.txt
3.txt

sent 99 bytes  received 82 bytes  362.00 bytes/sec
total size is 0  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
1.txt
2.txt
3.txt

sent 99 bytes  received 45 bytes  288.00 bytes/sec
total size is 0  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脚本,应该是可以满足一般的小企业了。

原文地址:https://www.cnblogs.com/alongdidi/p/rsync.html