rsync用法及参数详解

rsync

rsync的目的是实现本地主机和远程主机上的文件同步(包括本地推到远程,远程拉到本地两种同步方式),也可以实现本地不同路径下文件的同步,但不能实现远程路径1到远程路径2之间的同步(scp可以实现)。

不考虑rsync的实现细节,就文件同步而言,涉及了源文件和目标文件的概念,还涉及了以哪边文件为同步基准。例如,想让目标主机上的文件和本地文件保持同步,则是以本地文件为同步基准,将本地文件作为源文件推送到目标主机上。反之,如果想让本地主机上的文件和目标主机上的文件保持同步,则目标主机上的文件为同步基准,实现方式是将目标主机上的文件作为源文件拉取到本地。当然,要保持本地的两个文件相互同步,rsync也一样能实现,这就像Linux中cp命令一样,以本地某文件作为源,另一文件作为目标文件,但请注意,虽然rsync和cp能达到相同的目的,但它们的实现方式是不一样的。

既然是文件同步,在同步过程中必然会涉及到源和目标两文件之间版本控制的问题,例如是否要删除源主机上没有但目标上多出来的文件,目标文件比源文件更新(newer than source)时是否仍要保持同步,遇到软链接时是拷贝软链接本身还是拷贝软链接所指向的文件,目标文件已存在时是否要先对其做个备份等等。

rsync同步过程中由两部分模式组成:决定哪些文件需要同步的检查模式以及文件同步时的同步模式。

(1).检查模式是指按照指定规则来检查哪些文件需要被同步,例如哪些文件是明确被排除不传输的。默认情况下,rsync使用"quick check"算法快速检查源文件和目标文件的大小、mtime(修改时间)是否一致,如果不一致则需要传输。当然,也可以通过在rsync命令行中指定某些选项来改变quick check的检查模式,比如"--size-only"选项表示"quick check"将仅检查文件大小不同的文件作为待传输文件。rsync支持非常多的选项,其中检查模式的自定义性是非常有弹性的。

(2).同步模式是指在文件确定要被同步后,在同步过程发生之前要做哪些额外工作。例如上文所说的是否要先删除源主机上没有但目标主机上有的文件,是否要先备份已存在的目标文件,是否要追踪链接文件等额外操作。rsync也提供非常多的选项使得同步模式变得更具弹性。

相对来说,为rsync手动指定同步模式的选项更常见一些,只有在有特殊需求时才指定检查模式,因为大多数检查模式选项都可能会影响rsync的性能。

rsync 的命令格式(用法)为:

1)本地使用:
rsync [OPTION...] SRC... [DEST]
 
2)通过远程 Shell 使用:
拉: rsync [OPTION...] [USER@]HOST:SRC... [DEST]
推: rsync [OPTION...] SRC... [USER@]HOST:DEST
 
3)访问 rsync 服务器:
拉: rsync [OPTION...] [USER@]HOST::SRC... [DEST]
推: rsync [OPTION...] SRC... [USER@]HOST::DEST
拉: rsync [OPTION...] rsync://[USER@]HOST[:PORT]/SRC... [DEST]
推: rsync [OPTION...] SRC... rsync://[USER@]HOST[:PORT]/DEST

其中:

  • SRC: 是要复制的源位置

  • DEST: 是复制目标位置

  • 若本地登录用户与远程主机上的用户一致,可以省略 USER@

  • 使用远程 shell 同步时,主机名与资源之间使用单个冒号“:”作为分隔符

  • 使用 rsync 服务器同步时,主机名与资源之间使用两个冒号“::”作为分隔符

  • 当访问 rsync 服务器时也可以使用 rsync:// URL

  • “拉”复制是指从远程主机复制文件到本地主机

  • “推”复制是指从本地主机复制文件到远程主机

  • 当进行“拉”复制时,若指定一个 SRC 且省略 DEST,则只列出资源而不进行复制

    下面列出常用选项:

    选项说明
    -a, ––archive 归档模式,表示以递归方式传输文件,并保持所有文件属性,等价于 -rlptgoD (注意不包括 -H)
    -r, ––recursive 对子目录以递归模式处理
    -l, ––links 保持符号链接文件
    -H, ––hard-links 保持硬链接文件
    -p, ––perms 保持文件权限
    -t, ––times 保持文件时间信息
    -g, ––group 保持文件属组信息
    -o, ––owner 保持文件属主信息 (super-user only)
    -D 保持设备文件和特殊文件 (super-user only)
    -z, ––compress 在传输文件时进行压缩处理
    ––exclude=PATTERN 指定排除一个不需要传输的文件匹配模式
    ––exclude-from=FILE 从 FILE 中读取排除规则
    ––include=PATTERN 指定需要传输的文件匹配模式
    ––include-from=FILE 从 FILE 中读取包含规则
    ––copy-unsafe-links 拷贝指向SRC路径目录树以外的链接文件
    ––safe-links 忽略指向SRC路径目录树以外的链接文件(默认)
    ––existing 仅仅更新那些已经存在于接收端的文件,而不备份那些新创建的文件
    ––ignore-existing 忽略那些已经存在于接收端的文件,仅备份那些新创建的文件
    -b, ––backup 当有变化时,对目标目录中的旧版文件进行备份
    ––backup-dir=DIR 与 -b 结合使用,将备份的文件存到 DIR 目录中
    ––link-dest=DIR 当文件未改变时基于 DIR 创建硬链接文件
    ––delete 删除那些接收端还有而发送端已经不存在的文件
    ––delete-before 接收者在传输之前进行删除操作 (默认)
    ––delete-during 接收者在传输过程中进行删除操作
    ––delete-after 接收者在传输之后进行删除操作
    ––delete-excluded 在接收方同时删除被排除的文件
    -e, ––rsh=COMMAND 指定替代 rsh 的 shell 程序
    ––ignore-errors 即使出现 I/O 错误也进行删除
    ––partial 保留那些因故没有完全传输的文件,以是加快随后的再次传输
    ––progress 在传输时显示传输过程
    -P 等价于 ––partial ––progress
    ––delay-updates 将正在更新的文件先保存到一个临时目录(默认为 “.~tmp~”),待传输完毕再更新目标文件
    -v, ––verbose 详细输出模式
    -q, ––quiet 精简输出模式
    -h, ––human-readable 输出文件大小使用易读的单位(如,K,M等)
    -n, ––dry-run 显示哪些文件将被传输
    ––list-only 仅仅列出文件而不进行复制
    ––rsyncpath=PROGRAM 指定远程服务器上的 rsync 命令所在路径
    ––password-file=FILE 从 FILE 中读取口令,以避免在终端上输入口令,通常在 cron 中连接 rsync 服务器时使用
    -4, ––ipv4 使用 IPv4
    -6, ––ipv6 使用 IPv6
    ––version 打印版本信息
    ––help 显示帮助信息
  • 若使用普通用户身份运行 rsync 命令,同步后的文件的属主将改变为这个普通用户身份。

  • 若使用超级用户身份运行 rsync 命令,同步后的文件的属主将保持原来的用户身份。

 

rsync 的基本使用

在本地磁盘同步数据

# rsync -a --delete /home /backups
# rsync -a --delete /home/ /backups/home.0

在指定复制源时,路径是否有最后的 “/” 有不同的含义,例如:

  • /home : 表示将整个 /home 目录复制到目标目录
  • /home/ : 表示将 /home 目录中的所有内容复制到目标目录

使用基于 ssh 的 rsync 远程同步数据

同步静态主机表文件
# 执行“推”复制同步(centos5 是可解析的远程主机名)
[root@soho ~]# rsync /etc/hosts centos5:/etc/hosts

# 执行“拉”复制同步(soho 是可解析的远程主机名)
[root@centos5 ~]# rsync soho:/etc/hosts /etc/hosts
同步用户的环境文件
# 执行“推”复制同步
[osmond@soho ~]$ rsync ~/.bash* centos5:

# 执行“拉”复制同步
[osmond@cnetos5 ~]$ rsync soho:~/.bash* .
同步站点根目录
# 执行“推”复制同步
[osmond@soho ~]$ rsync -avz --delete /var/www root@192.168.0.101:/var/www

# 执行“拉”复制同步
[osmond@cnetos5 ~]$ rsync -avz --delete root@192.168.0.55:/var/www /var/www
  • 使用基于 ssh 的 rsync 同步数据可以使用 -e ssh 参数,当前的 CentOS 默认指定使用 ssh 作为远程Shell。若您在其他系统上执行 rsync 命令,为确保使用 ssh 作为远程 Shell,请添加 -e ssh 参数。
  • 通常 rsync 命令在后台以 cron 任务形式执行,为了避免从终端上输入口令需要设置 ssh。ssh 的设置方法请参考 安全登录守护进程。

使用 rsync 从远程 rsync 服务器同步数据

下面以镜像 CentOS 和 Ubuntu 的软件库为例来说明。
您可以到如下站点查找离自己最近的提供 rsync 服务的镜像站点

然后执行类似如下命令:

rsync -aqzH --delete --delay-updates \
rsync://mirror.centos.net.cn/centos /var/www/mirror/centos
rsync -azH --progress --delete --delay-updates \
rsync://ubuntu.org.cn/ubuntu /var/www/mirror/ubuntu/
rsync -azH --progress --delete --delay-updates \
rsync://ubuntu.org.cn/ubuntu-cn /var/www/mirror/ubuntu-cn/

为了每天不断更新,可以安排一个 cron 任务:

# crontab -e
# mirror centos at 0:10AM everyday
10 0 * * * rsync -aqzH --delete --delay-updates rsync://mirror.centos.net.cn/centos /var/www/mirror/centos/
# mirror ubuntu at 2:10AM everyday
10 2 * * * rsync -azH --progress --delete --delay-updates rsync://ubuntu.org.cn/ubuntu /var/www/mirror/ubuntu/
# mirror ubuntu-cn at 4:10AM everyday
10 4 * * * rsync -azH --progress --delete --delay-updates rsync://ubuntu.org.cn/ubuntu-cn /var/www/mirror/ubuntu-cn/

如果您安装了自己的匿名 rsync 服务器请相应地更改 rsync URL。有关如何配置匿名 rsync 服务器的内容请参见下节。

筛选 rsync 的传输目标

 

使用 --exclude/--include 选项

可以使用 ––exclude 选项排除源目录中要传输的文件;同样地,也可以使用 ––include 选项指定要传输的文件。
例如:下面的 rsync 命令将 192.168.0.101 主机上的 /www 目录(不包含 /www/logs 和 /www/conf子目录)复制到本地的 /backup/www/ 。

# rsync -vzrtopg --delete --exclude "logs/" --exclude "conf/" --progress \
backup@192.168.0.101:/www/ /backup/www/

又如:下面的 rsync 命令仅复制目录结构而忽略掉目录中的文件。

# rsync -av --include '*/' --exclude '*' \
backup@192.168.0.101:/www/ /backup/www-tree/

选项 ––include 和 ––exclude 都不能使用间隔符。例如:

--exclude "logs/" --exclude "conf/"

不能写成

--exclude "logs/ conf/"

使用 --exclude-from/--include-from 选项

当 include/exclude 的规则较复杂时,可以将规则写入规则文件。使用规则文件可以灵活地选择传输哪些文件(include)以及忽略哪些文件(exclude)。

  • 若文件/目录在剔除列表中,则忽略传输
  • 若文件/目录在包含列表中,则传输之
  • 若文件/目录未被提及,也传输之

在 rsync 的命令行中使用 ––exclude-from=FILE 或 ––include-from=FILE 读取规则文件。
规则文件 FILE 的书写约定:

  • 每行书写一条规则 RULE
  • 以 # 或 ; 开始的行为注释行

包含(include)和排除(exclude)规则的语法如下:

  • include PATTERN 或简写为 + PATTERN
  • exclude PATTERN 或简写为 - PATTERN

PATTERN 的书写规则如下:

  • 以 / 开头:匹配被传输的跟路径上的文件或目录
  • 以 / 结尾:匹配目录而非普通文件、链接文件或设备文件
  • 使用通配符
  • *:匹配非空目录或文件(遇到 / 截止)
  • **:匹配任何路径(包含 / )
  • ?:匹配除了 / 的任意单个字符
  • [:匹配字符集中的任意一个字符,如 [a-z] 或 [[:alpha:]]
  • 可以使用转义字符 \ 将上述通配符还原为字符本身含义

下面给出几个使用规则的例子:
例1:

# 不传输所有后缀为 .o 的文件
- *.o

# 不传输传输根目录下名为 foo 的文件或目录
- /foo

# 不传输名为 foo 的目录
- foo/

# 不传输 /foo 目录下的名为 bar 的文件或目录
- /foo/bar

例2:

# 传输所有目录和C语言源文件并禁止传输其他文件
+ */
+ *.c
- *

例3:

# 仅传输 foo 目录和其下的 bar.c 文件
+ foo/
+ foo/bar.c
- *

将规则写入规则文件之后,如何在命令行上使用它呢?下面给出一个例子:
首先将下面的规则存入名为 www-rsync-rules 的文件

# 不传输 logs 目录
- logs/

# 不传输后缀为 .tmp 的文件
- *.tmp

# 传输 Apache 虚拟主机文档目录(/*/ 匹配域名)
+ /srv/www/
+ /srv/www/*/
+ /srv/www/*/htdocs/
+ /srv/www/*/htdocs/**

# 传输每个用户的 public_html 目录(/*/ 匹配用户名)
+ /home/
+ /home/*/
+ /home/*/public_html/
+ /home/*/public_html/**
# 禁止传输其他
- *

然后即可使用类似如下的 rsync 命令:

rsync -av --delete --exclude-from=www-rsync-rules / remotehost:/dest/dir

rsync 应用示例

 

使用 rsync 镜像

使用 rsync 对目录做镜像实际上就是做无历史归档的完全备份。下面给出一个镜像远程 Web 站点例子。
笔者在 dreamhost 上维护了3个 Dokuwiki 站点。为了备份这3个站点笔者使用 rsync 进行镜像。远程站点的目录结构如下:

~
|-- sinosmond.com
| `-- dokuwiki
|-- smartraining.cn
| `-- dokuwiki
`-- symfony-project.cn
`-- dokuwiki

每个 Dokuwiki 的目录结构如下:

dokuwiki
|-- bin
|-- inc
|-- conf --- 存放配置文件的目录
| |-- acl.auth.php --- 访问控制配置文件 ★
| |-- local.php --- 本地配置文件 ★
| |-- users.auth.php --- 用户口令文件 ★
| `-- ………………
|-- data --- 存放数据的目录
| |-- attic --- 存放WIKI版本信息 ★
| |-- cache --- 存放数据缓存
| |-- index --- 存放站内索引
| |-- locks --- 存放编辑页面时的锁定文件
| |-- media --- 存放图片等 ★
| |-- meta --- 存放 meta 以便系统读取这些信息生成页面 ★
| `-- pages --- 存放 wiki 页面 ★
`-- lib
|-- plugins --- 存放插件的目录 ☆
|-- tpl --- 存放模版的目录 ☆
`-- ………………

为了减少网络流量,只同步标有 ★ 的目录或文件。若在站点运行过程中新安装了插件或更换了模板,也应该同步标有 ☆ 的目录。为此编写如下的规则文件 /root/bin/backup/dw-exclude.txt:

- dokuwiki/bin/
- dokuwiki/inc/
- dokuwiki/data/cache/
- dokuwiki/data/locks/
- dokuwiki/data/index/
+ dokuwiki/conf/acl.auth.php
+ dokuwiki/conf/local.php
+ dokuwiki/conf/users.auth.php
- dokuwiki/conf/*
+ dokuwiki/lib/plugins/

# 不同步系统默认安装的插件
- dokuwiki/lib/plugins/acl/
- dokuwiki/lib/plugins/config/
- dokuwiki/lib/plugins/importoldchangelog/
- dokuwiki/lib/plugins/importoldindex/
- dokuwiki/lib/plugins/info/
- dokuwiki/lib/plugins/plugin/
- dokuwiki/lib/plugins/revert/
- dokuwiki/lib/plugins/usermanager/
- dokuwiki/lib/plugins/action.php
- dokuwiki/lib/plugins/admin.php
- dokuwiki/lib/plugins/syntax.php
+ dokuwiki/lib/tpl

# 不同步系统默认安装的模板
- dokuwiki/lib/tpl/default/
- dokuwiki/lib/*
- dokuwiki/COPYING
- dokuwiki/doku.php
- dokuwiki/feed.php
- dokuwiki/index.php
- dokuwiki/install*
- dokuwiki/README
- dokuwiki/VERSION

下面是同步脚本 /root/bin/backup/rsync-dw.sh

#!/bin/bash
#####################################
# mirror dokuwiki website
# $1 --- domain (ex: smartraining.cn)
# $2 --- full or update
#####################################
# declare some variable
RmtUser=osmond
RmtIP=208.113.163.110
RmtPath=$1/dokuwiki
BackupRoot=/backups/$1
Excludes="--exclude-from=/root/bin/backup/dw-exclude.txt"

# use rsync for mirror
if [ "$2" == "full" ]
then

[ -d /backups/$1 ] || mkdir -p /backups/$1
excludesfile="/tmp/first-excludes"
cat > ${excludesfile} << EOF
+ dokuwiki/data/cache/_dummy
- dokuwiki/data/cache/*
+ dokuwiki/data/locks/_dummy
- dokuwiki/data/locks/*
+ dokuwiki/data/index/_dummy
- dokuwiki/data/index/*
EOF
/usr/bin/rsync -avzP --exclude-from=${excludesfile} \
$RmtUser@$RmtIP:$RmtPath $BackupRoot

else
/usr/bin/rsync -avzP --delete $Excludes \
$RmtUser@$RmtIP:$RmtPath $BackupRoot

fi

首次备份可以使用类似如下的命令(为了在本地保留一个完整复本):

# /root/bin/backup/rsync-dw.sh smartraining.cn full
# /root/bin/backup/rsync-dw.sh sinosmond.com full
# /root/bin/backup/rsync-dw.sh symfony-project.cn full

可以安排 cron 任务以便日后更新:

# crontab -e
05 1 * * * /root/bin/backup/rsync-dw.sh smartraining.cn
25 1 * * * /root/bin/backup/rsync-dw.sh sinosmond.com
45 1 * * * /root/bin/backup/rsync-dw.sh symfony-project.cn

普通型增量备份

使用 rsync 可以做增量备份。rsync 提供了 -b ––backup-dir 选项,使用这个选项可以将有变化的文件进行更新同时将其旧版本保存在指定的目录中,从而实现增量备份。 下面是对 /home 进行增量备份的步骤说明:

# 第0次备份
# 首先复制 /home 目录的内容到备份目录 /backups/daily/home.0,
# rsync -a /home/ /backups/daily/home.0
# /backups/daily/home.0 总是同步到最新的状态,可以每隔一段时间(如一周)
# 对其内容进行打包压缩生成归档文件(完全备份)存在 /backups/archive/。

# 第1次备份(此为核心操作)
# 将 /home 目录的内容同步到目录 /backups/daily/home.0,
# 并将有变化的文件的旧版本保存到 /backups/daily/home.1,
# 若每天执行一次,则目录 /backups/daily/home.1 保存了有变化文件一天前的状态。
# rsync -a --delete -b --backup-dir=/backups/daily/home.1 /home/ /backups/daily/home.0

# 第2次备份
# 将备份目录 /backups/daily/home.1 更名为 /backups/daily/home.2
# mv /backups/daily/home.1 /backups/daily/home.2
# 执行第1次备份的核心操作

# 第n次备份
# 将早先的备份目录 /backups/daily/home.n 到 /backups/daily/home.1
# 依次更名为 /backups/daily/home.(n+1) 到 /backups/daily/home.2
# 执行第1次备份的核心操作

下面给出一个增量备份示例脚本。

#!/bin/bash
#========================
# 您可以安排 cron 任务执行本脚本
# > crontab -e
#
# daily : 1 1 * * * /path/to/script/rsync-backup.sh
#========================
mydate="`date '+%Y%m%d.%H%M'`"

# Define rmt location
RmtUser=root
RmtHost=192.168.0.55
RmtPath=/home/
BackupSource="${RmtUser}@${RmtHost}:${RmtPath}"
#BackupSource="/home/"             # 若进行本地备份则用本地路径替换上面的行
# Define location of backup
BackupRoot="/backups/$RmtHost/"
# BackupRoot="/backups/localhost/" # 若进行本地备份则用本地路径替换上面的行
LogFile="${BackupRoot}/backup.log"
ExcludeList="/root/backup/backup-exclude-list.txt"
BackupName='home'
BackupNum="7"                      # 指定保留多少个增量备份(适用于每周生成归档文件)
#BackupNum="31"                    # 指定保留多少个增量备份(适用于每月生成归档文件)

# 定义函数检查目录 $1 是否存在,若不存在创建之
checkDir() {
    if [ ! -d "${BackupRoot}/$1" ] ; then
        mkdir -p "${BackupRoot}/$1"
    fi
}
# 定义函数实现目录滚动
# $1 -> 备份路径
# $2 -> 备份名称
# $3 -> 增量备份的数量
rotateDir() {
    for i in `seq $(($3 - 1)) -1 1`
    do
        if [ -d "$1/$2.$i" ] ; then
            /bin/rm -rf "$1/$2.$((i + 1))"
            mv "$1/$2.$i" "$1/$2.$((i + 1))"
        fi
    done
}

# 调用函数 checkDir ,确保目录存在
checkDir "archive"
checkDir "daily"

#======= Backup Begin =================
# S1: Rotate daily.
rotateDir "${BackupRoot}/daily" "$BackupName" "$BackupNum"

checkDir "daily/${BackupName}.0/"
checkDir "daily/${BackupName}.1/"

mv ${LogFile} ${BackupRoot}/daily/${BackupName}.1/

cat >> ${LogFile} <<_EOF
===========================================
    Backup done on: $mydate
===========================================
_EOF

# S2: Do the backup and save difference in ${BackupName}.1
rsync -av --delete \
    -b --backup-dir=${BackupRoot}/daily/${BackupName}.1 \
    --exclude-from=${ExcludeList} \
    $BackupSource ${BackupRoot}/daily/${BackupName}.0 \
    1>> ${LogFile} 2>&1

# S3: Create an archive backup every week
if [ `date +%w` == "0" ] # 每周日做归档
# if [ `date +%d` == "01" ] # 每月1日做归档
then
    tar -cjf ${BackupRoot}/archive/${BackupName}-${mydate}.tar.bz2 \
      -C ${BackupRoot}/daily/${BackupName}.0 .
fi

您可以适当修该上述脚本中变量:

RmtPath="$1/"
#BackupSource="$1/"
BackupName="$1"

然后传递脚本参数备份其他目录,例如要备份 /www 可以使用如下命令:

./rsync-backup.sh /www

快照型增量备份

使用 rsync 可以做快照(Snapshot)型增量备份。每一个快照都相当于一个完全备份。其核心思想是:对有变化的文件进行复制;对无变化的文件创建硬链接以减少磁盘占用。
下面是对 /home 进行快照型增量备份的步骤说明:

# 第0次备份
# 首先复制 /home 目录的内容到备份目录 /backups/home.0
# rsync -a /home/ /backups/home.0

# 第1次备份(此为核心操作)
# 以硬链接形式复制 /backups/home.0 到 /backups/home.1
# cp -al /backups/home.0 /backups/home.1
# 将 /home 目录的内容同步到目录 /backups/home.0
# (rsync 在发现变化的文件时,先删除之,然后在创建该文件)
# rsync -a --delete /home/ /backups/home.0

# 第2次备份
# 将备份目录 /backups/home.1 更名为 /backups/home.2
# mv /backups/home.1 /backups/home.2
# 执行第1次备份的核心操作

# 第n次备份
# 将早先的备份目录 /backups/home.n 到 /backups/home.1
# 依次更名为 /backups/home.(n+1) 到 /backups/home.2
# 执行第1次备份的核心操作

rsync 2.5.6 版本之后提供了 ––link-dest 选项,如下两条核心操作命令:

cp -al /backups/home.0 /backups/home.1
rsync -a --delete /home/ /backups/home.0

可以简化为如下的一条命令:

rsync -a --delete --link-dest=/backups/home.1 /home/ /backups/home.0

下面给出一个快照型增量备份示例脚本,该脚本来自http://www.mikerubel.org/computers/rsync_snapshots/contributed/peter_schneider-kamp

#!/bin/bash
# ----------------------------------------------------------------------
# mikes handy rotating-filesystem-snapshot utility
# ----------------------------------------------------------------------
# RCS info: $Id: make_snapshot.sh,v 1.6 2002/04/06 04:20:00 mrubel Exp $
# ----------------------------------------------------------------------
# this needs to be a lot more general, but the basic idea is it makes
# rotating backup-snapshots of /home whenever called
# ----------------------------------------------------------------------

# ------------- system commands used by this script --------------------
ID='/usr/bin/id';
ECHO='/bin/echo';

MOUNT='/bin/mount';
RM='/bin/rm';
MV='/bin/mv';
CP='/bin/cp';
TOUCH='/usr/bin/touch';

RSYNC='/usr/bin/rsync';

# ------------- file locations -----------------------------------------

MOUNT_DEVICE=/dev/hdb1;
SNAPSHOT_RW=/root/snapshots;
EXCLUDES=/etc/snapshot_exclude;

# ------------- backup configuration------------------------------------

BACKUP_DIRS="/etc /home"
NUM_OF_SNAPSHOTS=3
BACKUP_INTERVAL=hourly

# ------------- the script itself --------------------------------------

# make sure we're running as root
if (( `$ID -u` != 0 )); then { $ECHO "Sorry, must be root. Exiting..."; exit; } fi

echo "Starting snapshot on "`date`

# attempt to remount the RW mount point as RW; else abort
$MOUNT -o remount,rw $MOUNT_DEVICE $SNAPSHOT_RW ;
if (( $? )); then
{
	$ECHO "snapshot: could not remount $SNAPSHOT_RW readwrite";
	exit;
}
fi;

# rotating snapshots
for BACKUP_DIR in $BACKUP_DIRS
do
	NUM=$NUM_OF_SNAPSHOTS
	# step 1: delete the oldest snapshot, if it exists:
	if [ -d ${SNAPSHOT_RW}${BACKUP_DIR}/${BACKUP_INTERVAL}.$NUM ] ; then \
	$RM -rf ${SNAPSHOT_RW}${BACKUP_DIR}/${BACKUP_INTERVAL}.$NUM ; \
	fi ;
	NUM=$(($NUM-1))
	# step 2: shift the middle snapshots(s) back by one, if they exist
	while [[ $NUM -ge 1 ]]
	do
		if [ -d ${SNAPSHOT_RW}${BACKUP_DIR}/${BACKUP_INTERVAL}.$NUM ] ; then \
			$MV ${SNAPSHOT_RW}${BACKUP_DIR}/${BACKUP_INTERVAL}.$NUM ${SNAPSHOT_RW}${BACKUP_DIR}/${BACKUP_IN}
		fi;
		NUM=$(($NUM-1))
	done

	# step 3: make a hard-link-only (except for dirs) copy of the latest snapshot,
	# if that exists
	if [ -d ${SNAPSHOT_RW}${BACKUP_DIR}/${BACKUP_INTERVAL}.0 ] ; then \
		$CP -al ${SNAPSHOT_RW}${BACKUP_DIR}/${BACKUP_INTERVAL}.0 ${SNAPSHOT_RW}${BACKUP_DIR}/${BACKUP_INTERVAL}
	fi;
	# step 4: rsync from the system into the latest snapshot (notice that
	# rsync behaves like cp --remove-destination by default, so the destination
	# is unlinked first. If it were not so, this would copy over the other
	# snapshot(s) too!
	$RSYNC \
		-va --delete --delete-excluded \
		--exclude-from="$EXCLUDES" \
		${BACKUP_DIR}/ ${SNAPSHOT_RW}${BACKUP_DIR}/${BACKUP_INTERVAL}.0 ;
	# step 5: update the mtime of ${BACKUP_INTERVAL}.0 to reflect the snapshot time
	$TOUCH ${SNAPSHOT_RW}${BACKUP_DIR}/${BACKUP_INTERVAL}.0 ;
done

# now remount the RW snapshot mountpoint as readonly

$MOUNT -o remount,ro $MOUNT_DEVICE $SNAPSHOT_RW ;
if (( $? )); then
{
	$ECHO "snapshot: could not remount $SNAPSHOT_RW readonly";
	exit;
} fi;

摘自:

https://www.cnblogs.com/noxy/p/8986164.html

http://www.cnblogs.com/f-ck-need-u/p/7220009.html

posted @ 2020-11-30 10:17  553490191  阅读(834)  评论(0编辑  收藏  举报