Top

D15:常见任务和基本工具之五

归档和备份

本章涉及的命令有: 目录:

压缩文件

数据压缩是从数据中消除冗余的过程。压缩算法(用于执行压缩的数学技术)分为两大类: 由于计算机上大多数数据都不能容忍任何数据丢失,所以这里只研究无损压缩。

gzip

gzip程序用于压缩一个或多个文件。执行时,它会将原始文件替换为原始文件的压缩版本。
相应的,gunzip程序用于将压缩文件恢复为原始的未压缩形式。
举例:
[me@linuxbox ~]$ ls -l /etc > foo.txt
[me@linuxbox ~]$ ls -l foo.*
-rw-r--r-- 1 me   me   15738 2018-10-14 07:15 foo.txt
[me@linuxbox ~]$ gzip foo.txt
[me@linuxbox ~]$ ls -l foo.*
-rw-r--r-- 1 me   me   3230 2018-10-14 07:15 foo.txt.gz
[me@linuxbox ~]$ gunzip foo.txt
[me@linuxbox ~]$ ls -l foo.*
-rw-r--r-- 1 me   me   15738 2018-10-14 07:15 foo.txt	
gzip常用选项:
选项 长选项 说明
-c --stdout
--to-stdout
将输出写入标准输出并保留原始文件。
-d --decompress
--uncompress
解压缩。与使用gunzip相似。
-f --force 即使原始文件的压缩版本已经存在,也要强制压缩。
-h --help 显示使用帮助。
-l --list 列出每个压缩文件的压缩统计信息
-r --recursive 如果命令行上的一个或多个参数是目录,则递归压缩其中包含的文件。
-t --test 测试压缩文件的完整性。
-v --verbose 压缩时显示详细消息。
-number 设置压缩量。
number介于1(最快、压缩比最小)到9(最慢、压缩比最大)之间。值1和9也可以分别替代为--fast和--best。
默认值为6。
gzip可以通过标准输入和输出以有趣的方式使用:
[me@linuxbox ~]$ ls -s /etc | gzip > foo.txt.gz
以上命令可以直接生成压缩过的文件。

gzipgunzip假定文件名以扩展名.gz结尾,因此无需指定它,只要指定的名称与现有的解压缩文件不冲突。

如果只是要查看压缩文本文件的内容,可以使用以下命令:
[me@linuxbox ~]$ gunzip -c foo.txt | less
或者可以使用gzip提供的一个名为zcat的程序,它相当于带了-c选项的gunzip。它可以像cat命令一样用于gzip压缩文件:
[me@linuxbox ~]$ zcat foo.txt.gz | less
另外还有一个zless程序可以实现类似的效果。

bzip2

Julian Seward写的bzip2程序与gzip类似,但使用了不同的压缩算法,以牺牲压缩速度为代价实现更高的压缩比。
大多数情况下,它的工作方式与gzip相同。
bzip2压缩的文件扩展名为.bz2。
[me@linuxbox ~]$ ls -l /etc > foo.txt
[me@linuxbox ~]$ ls -l foo.txt
-rw-r--r-- 1 me   me   15738 2018-10-17 13:51 foo.txt
[me@linuxbox ~]$ bzip2 foo.txt
[me@linuxbox ~]$ ls -l foo.txt.bz2
-rw-r--r-- 1 me   me   2792 2018-10-17 13:51 foo.txt.bz2
[me@linuxbox ~]$ bunzip2 foo.txt.bz2	
bzip2不支持-r选项,且-number的含义与gzip有些差异。
bzip2附带用于解压缩文件的bunzip2bcat
bzip2还附带了bz2recover程序,用于尝试修复损坏的bz2文件。

不要强迫自己

对已经压缩过的文件进行压缩,通常会得到更大的文件。

归档文件

归档通常与压缩结合使用。
归档将许多文件收集起来,并将它们捆绑到一个大文件中。

tar

tar是tape archive(磁带存档)的缩写。
一般来说,扩展名为.tar的文件表示普通tar存档;扩展名.tgz表示gzip存档。
tar存档可以由一组单独的文件、一个或多个目录层次结构或两者的混合组成。
语法如下:
tar mode [optinos] pathname ...
mode可以是以下之一:
模式 解释
c 从文件和/或目录列表创建归档
x 提取归档文件
r 将指定的路径名附加到归档的末尾
t 列出归档的包含的内容
举例:
[me@linuxbox ~]$ mkdir -p playground/dir-{001..100}
[me@linuxbox ~]$ touch playground/dir-{001..100}/file-{A..Z}
然后,创建一个归档:
[me@linuxbox ~]$ tar cf playground.tar playground
此命令创建一个名为playground.tar的归档文件,它包含整个playground目录层次结构。modec和optionf连在一起,中间没有破折号。
但是需要注意,mode必须在前面,然后才能指定其他选项。

要查看归档包里的内容,可以使用以下命令:
[me@linuxbox ~]$ tar tf playground.tar
添加v选项可以查看更详细信息:
[me@linuxbox ~]$ tar tvf playground.tar

下面将归档的文件解出至新的地方:
[me@linuxbox ~]$ mkdir foo
[me@linuxbox ~]$ cd foo
[me@linuxbox foo]$ tar xf ../playground.tar
[me@linuxbox foo]$ ls
playground	

通常路径名不支持通配符,但是提供了--wildcards选项可以使用通配符(FreeBSD中无此选项):
[me@linuxbox ~]$ tar xf ../playgound2.tar --wildcards 'home/me/playground/dir-*/file-A'
以上命令仅释放匹配指定路径的文件。

tar经常与find结合使用以生成档案:
[me@linuxbox ~]$ find playground -name 'file-A' -exec tar rf playground.tar '{}' '+'
以上示例使用find生成一组要包含在归档文件中的文件。
find中使用tar是创建目录树或整个系统的增量备份(incremental backups)的好方法。
通过使用find -newer timestamp -exec tar rf test.tar '{}' '+'来匹配比“timestamp”新的文件,可以创建一个只包含比上一个归档文件新的文件的归档文件。

tar还可以使用标准输入和输出。下面是一个全面的例子:
[me@linuxbox foo]$ cd
[me@linuxbox ~]$ find playground -name 'file-A' | tar cf - --files-from=- | gzip > playground.tgz	
此例中,使用find程序生成匹配文件的列表,并将它们传输到tar中。
如果指定了文件名-,则根据需要将其视为标准输入或输出。(使用-来表示标准输入/输出的管理也被其他程序使用)
--files-from选项(也可以指定为-T)使tar从文件而不是命令读取其路径名列表。
最后,tar生成的归档文件通过管道传输到gzip中,以创建压缩的归档文件playground.tgz。
.tgz扩展名是gzip压缩tar文件的常规扩展名。有时候也会使用.tar.gz扩展名。

虽然可以在外部使用gzip程序来生成压缩的归档文件,但GNUtar的现代版本分别使用zj选项直接支持gzipbzip2压缩。
前面的例子可以简化为:
[me@linuxbox ~]$ find playground -name 'file-A' | tar czf playground.tgz -T -
以下是使用bzip2压缩的示例:
[me@linuxbux ~]$ find playground -name 'file-A' | tar cjf playground.tbz -T -

tar命令的标准输入和输出的另一个有趣用法是通过网络在系统中传输文件:
[me@linuxbox ~]$ mkdir remote-stuff
[me@linuxbox ~]$ cd remote-stuff
[me@linuxbox remote-stuff]$ ssh remote-sys 'tar cf - Documents' | tar xf -
me@remote-sys’s password:
[me@linuxbox remote-stuff]$ ls
Documents	

zip

zip/unzip的主要用途时与Windows系统交换文件,而不是在Linux上执行压缩和归档,在Linux上,targzip才是首选。

基本格式(FreeBSD默认没有此软件,需要安装):
zip options zipfile file ...
例如:
[me@linuxbox ~]$ zip -r playground.zip playground
如果没有包含递归选项-r,则只存储目录而不存储其中的内容。
虽然扩展名.zip会自动添加,但为清晰起见,最好能添加文件扩展名。
创建zip压缩文件时,zip通常会显示一系列消息:
adding: playground/dir-020/file-Z (stored 0%)
adding: playground/dir-020/file-Y (stored 0%)
adding: playground/dir-020/file-X (stored 0%)
adding: playground/dir-087/ (stored 0%)
adding: playground/dir-087/file-S (stored 0%)	
使用unzip可以很简单的解压缩文件:
[me@linuxbox ~]$ cd foo
[me@linuxbox foo]$ unzip ../playground.zip	
tar相反,zip如果指定了现有的归档文件,它将会被更新而不是替换。

通过指定要解压的文件,可以有选择地从zip文件中列出和提取文件:
[me@linuxbox ~]$ unzip -l playground.zip playground/dir-087/file-Z
Archive: ../playground.zip
  Length    Date   Time    Name
 --------   ----   ----    ----
        0 10-05-16 09:25   playground/dir-087/file-Z
 --------                  -------
        0                  1 file
[me@linuxbox ~]$ cd foo
[me@linuxbox foo]$ unzip ../playground.zip playground/dir-087/file-Z
Archive: ../playground.zip
replace playground/dir-087/file-Z? [y]es, [n]o, [A]ll, [N]one,
[r]ename: y
 extracting: playground/dir-087/file-Z	
使用-l选项会导致unzip只列出归档文件的内容,而不提取文件。如果未指定任何文件,unzip将列出存档中的所有文件。
可以添加-v选项来增加列表的详细程度。
当存档提取与现有文件冲突时,会在替换文件之前提示用户。

tar一样,zip可以使用标准的输入和输出。可以通过-@选项将文件名列表传输到zip
[me@linuxbox foo]$ cd
[me@linuxbox ~]$ find playground -name "file-A" | zip -@ file-A.zip	
以上,使用find生成一个与测试名称“file-A”匹配的文件列表,然后将该列表导入zip,这将创建包含所选文件的存档“file-A.zip”。

zip还支持将其输出写入标准输出,但它的使用受到限制,因为几乎没有程序可以使用该输出。
不幸的是,unzip不接受标准输入。这可以防止zipunzip一起用于像tar一样执行网络文件复制。
然而,zip可以接受标准输入,因此可以用来压缩其他程序的输出:
[me@linuxbox ~]$ ls -l /etc/ | zip ls-etc.zip -
 adding: - (deflated 80%)	
以上例子中,通过管道将ls的输出传输到zip中。和tar一样,zip将尾随的破折号解释为“对输入文件使用标准输入”。
当指定-p(pipe管道)选项时,unzip允许将其输出发送到标准输出:
[me@linuxbox ~]$ unzip -p ls-etc.zip | less

同步文件和目录

rsync命令的格式为:
rsync options source destination
sourcedestination可以是以下一种: 注意:源文件或目标文件必须有一个是本地文件,不支持远程到远程复制

以下命令将playground目录对应的副本同步到foo目录中:
[me@linuxbox ~]$ rsync -av playground foo
其中-a选项用于归档——导致文件属性的递归和保存,-v选项表示详细输出(verbose output)。
当命令运行时,我们将看到被复制的文件和目录的列表,以及复制数量的摘要信息。

如果我们再执行一次,结果会有不同:
[me@linuxbox ~]$ rsync -av playground foo
building file list ... done

sent 22635 bytes  received 20 bytes  45310.00 bytes/sec
total size is 3230  speedup is 0.14
没有显示文件,这是因为rsync检测到~/playground~/foo/playground这两个目录完全相同,不需要复制任何文件。
如果我们修改一个playground中的文件,然后再运行rsync
[me@linuxbox ~]$ touch playground/dir-099/file-Z
[me@linuxbox ~]$ rsync -av playground foo
building file list ... done
playground/dir-099/file-Z

sent 22685 bytes  received 42 bytes  45454.00 bytes/sec
total size is 3230  speedup is 0.14
可以看到,rsync检测到变化并仅仅复制了更新的文件。

如果source后面带斜杠,则rsync仅复制源目录中的内容,而不复制源目录。例如:
[me@linuxbox ~]$ rsync source/ destination
而如果使用source/*,则复制源目录中的所有内容,包括隐藏的文件。

以下示例将重要内容同步到移动硬盘的backup目录:
[me@linuxbox ~]$ mkdir /media/BigDisk/backup
[me@linuxbox ~]$ sudo rsync -av --delete /etc /home /usr/local
/media/BigDisk/backup
以上示例将/etc、/home、/usr/local目录从系统复制到虚拟存储设备。
--delete选项用于删除备份设备上可能存在但源设备上不再存在的文件。
这种方法可以用来备份小型系统,也可以通过在.bashrc文件中创建别名来简化操作:
alias backup='sudo rsync -av --delete /etc /home /usr/local /media/BigDisk/backup'
然后运行backup即可。

在网络上使用rsync

rsync真正的优点之一是它可以通过网络复制文件。

实现远程复制的第一种方法是通过ssh完成远程复制。
以下例子假设远程主机名为remote-sys,其上有个名为/backup的目录:
[me@linuxbox ~]$ sudo rsync -av --delete --rsh=ssh /etc /home /usr/local remote-sys:/backup
--rsh=ssh选项告诉rsync命令使用ssh程序作为它的远程shell。此时,可以使用ssh加密通道安全传输数据到远程主机。
remote-sys:/backup则表示远程主机上的路径名。

第二种方法是通过rsync server来实现网络数据同步。
rsync可以以进程方式运行并监听同步传入请求。这样做通常是为了允许对远程系统进行镜像。
举例:
[me@linuxbox ~]$ mkdir fedora-devel
[me@linuxbox ~]$ rsync -av --delete rsync://archive.linux.duke.edu/fedora/linux/development/rawhide/Everything/x86_64/os/ fedora-devel