参考资料:骏马金龙的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
- 第一种方式是上述的Local部分,为本地主机内部的同步。
- 第二种方式是上述的remote shell部分,为不同主机通过远程shell实现同步。
- 第三种方式是上述的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/ [email protected]'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脚本,应该是可以满足一般的小企业了。