第十八章:归档和备份

系统管理员的主要任务之一是保护系统数据的安全。一种方法是及时备份系统文件。即使我们不是系统管理员,制作东西的副本并将大量文件从一个地方移动到另一个地方和从一个设备移动到一个设备通常也是有用的。

在本章中,我们将介绍用于管理文件集合的几个常见程序。

以下是文件压缩程序:

以下是归档程序:

以下是文件同步程序:

第十八章:归档和备份压缩文件gzipbzip2不要强迫自己归档文件tarzip同步文件和目录在网络上使用rsync总结

压缩文件

纵观计算的历史,人们一直在努力将最多的数据放入最小的可用空间,无论是内存、存储设备还是网络带宽。我们今天认为理所当然的许多数据服务,如手机服务、高清电视或宽带互联网,都归功于有效的数据压缩(data compression)技术。

数据压缩是从数据中删除冗余(redundancy)的过程。让我们考虑一个虚构的例子。假设我们有一个完全黑色的图片文件,尺寸为100像素乘100像素。就数据存储而言(假设24位,即每像素3个字节),图像将占用30000个字节的存储空间:

100 * 100 * 3 = 30,000

一个只有一种颜色的图像包含完全冗余的数据。如果我们足够聪明,我们可以对数据进行编码,这样我们就可以简单地描述我们有一个10000个黑色像素块的事实。因此,与其存储包含30000个零的数据块(黑色通常在图像文件中表示为零),我们可以将数据压缩为数字10000,然后加一个零来表示我们的数据。这种数据压缩方案称为行程编码(run-length encoding),是最基本的压缩技术之一。今天的技术更加先进和复杂,但基本目标仍然是一样的——摆脱冗余数据。

压缩算法(Compression algorithms ,用于执行压缩的数学技术)分为两大类。

在我们的讨论中,我们将专门研究无损压缩,因为计算机上的大多数数据都不能容忍任何数据丢失。

gzip

gzip 程序用于压缩一个或多个文件。执行时,它会用原始文件的压缩版本替换原始文件。相应的 gunzip 程序用于将压缩文件还原为其原始的未压缩形式。以下是一个示例:

在这个例子中,我们从目录列表中创建了一个名为 foo.txt 的文本文件。接下来,我们运行 gzip ,它将原始文件替换为名为 foo.txt.gz 的压缩版本。在 foo.* 的目录列表中,我们看到原始文件已被压缩版本替换,压缩版本的大小约为原始文件的五分之一。我们还可以看到压缩文件与原始文件具有相同的权限和时间戳。

接下来,我们运行 gunzip 程序来解压缩文件。之后,我们可以看到文件的压缩版本已被替换为原始版本,再次保留了权限和时间戳。

gzip 有很多选项,如下表所示:

选项长选项描述
-c--stdout
--to-stdout
将输出写入标准输出并保留原始文件。
-d--decompress
--uncompress
解压。这导致 gzip 的行为类似于 gunzip
-f--force即使原始文件的压缩版本已经存在,也会强制压缩。
-h--help显示使用信息。
-l--list列出每个压缩文件的压缩统计信息。
-r--resursive如果命令行上的一个或多个参数是目录,则递归压缩其中包含的文件。
-t--test测试压缩文件的完整性。
-v--verbose压缩时显示详细消息。
-number 设置压缩量。number 是1(最快、压缩最少)到9(最慢、压缩最多)范围内的整数。
值1和9也可以分别表示为 --fast 和 --best
默认值为6。

让我们回到早前的例子。

在这里,我们用名为 foo.txt.gz 的压缩版本替换了文件 foo.txt 。接下来,我们使用 -t-v 选项测试了压缩版本的完整性。最后,我们将文件解压缩为原始形式。

gzip 还可以通过标准输入和输出以有趣的方式使用。

此命令创建目录列表的压缩版本。

解压gzip文件的 gunzip 程序假定文件名以 .gz 扩展名结尾,因此只要指定的名称与现有的未压缩文件不冲突,就没有必要指定它。

如果我们的目标只是查看压缩文本文件的内容,我们可以这样做:

或者, gzip 提供了一个名为 zcat 的程序,它相当于带有 -c 选项的 gunzip 。它可以像 gzip 压缩文件上的 cat 命令一样使用。

小贴士:也有一个 zless 的程序。它执行与前一个管道相同的功能。

bzip2

Julian Seward的 bzip2 程序类似于 gzip ,但使用了不同的压缩算法,以压缩速度为代价实现了更高级别的压缩。在大多数方面,它的工作方式与 gzip 相同。用 bzip2 压缩的文件用扩展名 .bz2 表示。

正如我们所看到的, bzip2 的使用方式与 gzip 相同。 bzip2 也支持我们讨论的 gzip 的所有选项( -r 除外)。但是请注意,压缩级别选项( -number )与 bzip2 的含义略有不同。 bzip2 附带 bunzip2bzcat 用于解压缩文件。

bzip2 还附带了 bzip2recover 程序,该程序将尝试恢复损坏的 .bz2 文件。

不要强迫自己

我偶尔会看到人们试图通过以下方式压缩已经使用有效压缩算法压缩的文件:

别这样。你可能只是在浪费时间和空间!如果对已压缩的文件应用压缩,通常会得到一个更大的文件。这是因为所有的压缩技术都涉及一些开销,这些开销被添加到文件中以描述压缩。如果你试图压缩一个已经不包含冗余信息的文件,压缩通常不会带来任何节省来抵消额外的开销。

归档文件

通常与压缩结合使用的常见文件管理任务是归档(archiving)。归档是收集许多文件并将它们捆绑在一起形成一个大文件的过程。归档通常是系统备份的一部分。当旧数据从系统移动到某种类型的长期存储时,也会使用它。

tar

在类Unix的软件世界中, tar 程序是归档文件的经典工具。它的名字是磁带存档(tape archive)的缩写,揭示了它作为制作备份磁带的工具的根源。虽然它仍然用于传统任务,但它同样适用于其他存储设备。我们经常看到以 .tar.tgz 扩展名结尾的文件名,分别表示“plain”(纯)tar存档和gzip压缩的存档。tar存档可以由一组单独的文件、一个或多个目录层次结构或两者的混合组成。命令语法如下:

tar mode [ options ] pathname ...

这里的模式是下表中列出的以下操作模式之一(这里只显示了部分列表;完整列表请参阅 tar 手册页)。

模式描述
c从文件和/或目录列表创建存档。
x提取存档。
r将指定的路径名附加到存档的末尾。
t列出存档的内容。

tar 使用了一种稍微奇怪的方式来表示选项,所以我们需要一些例子来展示它是如何工作的。首先,让我们从上一章重新创建我们的游乐场。

接下来,让我们创建整个游乐场的tar存档。

此命令创建一个名为 playground.tar 的tar存档,其中包含整个游乐场目录层次结构。我们可以看到,mode和用于指定tar存档名称的 f 选项可以连接在一起,不需要前导破折号。但是请注意,在任何选项之前,必须始终先指定模式。

要列出存档的内容,我们可以这样做:

要获得更详细的列表,我们可以添加 v(verbose)选项:

现在,让我们把游乐场搬到一个新的地方。我们将通过创建一个名为 foo 的新目录,更改目录并提取tar存档来实现这一点。

如果我们检查 ~/foo/playground 的内容,我们会看到存档已成功安装,创建了原始文件的精确复制。然而,有一点需要注意。除非我们以超级用户身份操作,否则从档案中提取的文件和目录将归执行还原的用户所有,而不是原始所有者。

tar 的另一个有趣行为是它处理存档中路径名的方式。路径名的默认值是相对的,而不是绝对的。tar 在创建存档时,只需删除路径名中的任何前导斜线即可。为了演示,我们将重新创建我们的存档,这次指定一个绝对路径名。

记住,当我们按 Enter 键时,~/playground 将扩展为 /home/me/playground ,因此我们将获得演示的绝对路径名。接下来,我们将像以前一样提取存档,并观察会发生什么。

这里我们可以看到,当我们提取第二个存档时,它相对于我们当前的工作目录 ~/foo 重新创建了 home/me/playground 目录,而不是相对于根目录,就像绝对路径名的情况一样。这似乎是一种奇怪的工作方式,但实际上这种方式更有用,因为它允许我们将档案提取到任何位置,而不是被迫将其提取到原始位置。重复练习并包含详细选项(v)将更清楚地了解正在发生的事情。

让我们考虑一个假设但实际的 tar 应用示例。想象一下,我们想将主目录及其内容从一个系统复制到另一个系统,我们有一个大的USB硬盘可用于传输。在我们的现代Linux系统中,驱动器“自动”(automagically)挂载在 /media 目录中。让我们还想象一下,当我们附加磁盘时,磁盘的卷名为 BigDisk 。要制作tar存档,我们可以执行以下操作:

写入tar文件后,我们卸载驱动器并将其连接到第二台计算机。同样,它被挂载在 /media/BigDisk 上。要提取存档,我们这样做:

这里重要的是,我们必须首先将目录更改为 /,以便提取相对于根目录,因为存档中的所有路径名都是相对的。

提取存档时,可以限制从存档中提取的内容。例如,如果我们想从存档中提取一个文件,可以这样做:

tar xf archive.tar pathname

通过在命令中添加尾随路径名, tar 将仅还原指定的文件。可以指定多个路径名。请注意,路径名必须是存储在存档中的完整、精确的相对路径名。指定路径名时,通常不支持通配符;然而,GNU版本的 tar(在Linux发行版中最常见的版本)通过 --wildcards 选项支持它们。以下是一个使用我们之前的playground.tar文件的示例:

此命令将仅提取与指定路径名匹配的文件,包括通配符 dir -*

tar 经常与 find结 合使用以生成档案。在这个例子中,我们将使用 find 生成一组要包含在存档中的文件。

在这里,我们使用 find 来匹配名为 file-Aplayground 中的所有文件,然后使用 -exec 操作,在append模式(r)中调用tar,将匹配的文件添加到归档 playground.tar 中。

使用 tarfind 是创建目录树或整个系统的增量备份(incremental backups)的好方法。通过使用 find 匹配比时间戳文件更新的文件,我们可以创建一个只包含比上一个存档更新的文件的存档,假设时间戳文件在每个存档创建后都会立即更新。

tar 还可以使用标准输入和输出。下面是一个全面的例子:

在这个例子中,我们使用 find 程序生成一个匹配文件的列表,并将其管道传输到 tar 中。如果指定了文件名-,则根据需要将其视为标准输入或输出。(顺便说一句,许多其他程序也使用这种使用 - 表示标准输入/输出的约定)。 --files-from 选项(也可以指定为 -T )使 tar 从文件而不是命令行读取其路径名列表。最后, tar 生成的归档文件被管道传输到 gzip 中,以创建压缩的归档文件 playground.tgz.tgz 扩展名是 gzip 压缩 tar 文件的常规扩展名。有时也会使用扩展名 .tar.gz

虽然在上面的例子中,我们在外部使用 gzip 程序来生成压缩档案,但现代版本的GNU tar 分别使用 zj 选项直接支持 gzipbzip2 压缩。以前面的例子为基础,我们可以这样简化它:

如果我们想创建一个bzip2压缩档案,我们可以这样做:

通过简单地将压缩选项从 z 更改为 j (并将输出文件的扩展名更改为 .tbz 以表示bzip2压缩文件),我们启用了bzip2的压缩。

tar 命令的标准输入和输出的另一个有趣用途是通过网络在系统之间传输文件。想象一下,我们有两台机器运行一个配备 tarssh 的类Unix系统。在这种情况下,我们可以将一个目录从远程系统(在本例中称为 remote-sys )传输到本地系统。

在这里,我们能够将名为 Documents 的目录从远程系统 remote-sys 复制到本地系统上名为 remote-stuff 的目录中的一个目录。我们是怎么做到的?首先,我们使用 ssh 在远程系统上启动 tar 程序。您会记得, ssh 允许我们在联网的计算机上远程执行程序,并在本地系统上“查看”结果——远程系统上产生的标准输出被发送到本地系统进行查看。我们可以利用这一点,让 tar 创建一个存档(c模式)并将其发送到标准输出,而不是一个文件(带破折号参数的 f 选项),从而通过 ssh 提供的加密隧道将存档传输到本地系统。在本地系统上,我们执行 tar 并让它展开从标准输入(同样是带有破折号参数的 f 选项)提供的存档(x模式)。

zip

zip 程序既是压缩工具,也是归档工具。该程序使用的文件格式对Windows用户来说很熟悉,因为它可以读取和写入 .zip 文件。然而,在Linux中, gzip 是主要的压缩程序, bzip2 紧随其后。

在最基本的用法中, zip 的调用方式如下:

zip options zipfile file ...

例如,要制作游乐场的zip存档,我们可以这样做:

除非我们包含递归的 -r 选项,否则只存储 playground 目录(但不存储其任何内容)。虽然添加扩展名 .zip 是自动的,但为了清楚起见,我们将包含文件扩展名。

在创建zip存档的过程中, zip 通常会显示一系列这样的消息:

这些消息显示了添加到存档中的每个文件的状态。 zip 将使用两种存储方法之一将文件添加到存档中:要么它将“存储”(store)一个不压缩的文件,如上所示;要么它将对执行压缩的文件进行“压缩”(deflate)。存储方法后显示的数值表示实现的压缩量。由于我们的游乐场只包含空文件,因此不会对其内容进行压缩。

使用 unzip 程序提取zip文件的内容很简单。

关于 zip (与 tar 相反),需要注意的一点是,如果指定了现有的存档,它将被更新而不是替换。这意味着现有的存档将被保留,但会添加新文件并替换匹配的文件

通过指定要解压缩的文件,可以从zip存档中有选择地列出和提取文件。

使用 -l 选项会导致 unzip 仅列出存档的内容,而不提取文件。如果没有指定文件, unzip 将列出存档中的所有文件。可以添加 -v 选项以增加列表的详细程度。请注意,当存档提取与现有文件冲突时,在替换文件之前会提示用户。

tar 一样, zip 可以使用标准的输入和输出,尽管它的实现没有那么有用。可以通过 -@ 选项将文件名列表通过管道进行压缩。

在这里,我们使用 find 生成一个与测试 -name “file-a” 匹配的文件列表,然后将该列表导入 zip ,这将创建包含所选文件的归档 file-A.zip

zip 还支持将其输出写入标准输出,但它的使用受到限制,因为很少有程序可以使用该输出。不幸的是, unzip 程序不接受标准输入。这可以防止 zipunziptar 一样一起用于执行网络文件复制。

然而, zip 可以接受标准输入,因此可以用于压缩其他程序的输出。

在这个例子中,我们将 ls 的输出管道到 zip 中。与 tar 一样, zip 将尾部破折号解释为【使用输入文件的标准输入】。

当指定 -p (用于管道)选项时, unzip 程序允许将其输出发送到标准输出。

我们谈到了 zip/unzip 可以做的一些基本事情。它们都有很多增加灵活性的选项,尽管有些是特定于其他系统的平台。 zipunzip 的手册页都很好,包含有用的示例。然而,这些程序的主要用途是与Windows系统交换文件,而不是在Linux上执行压缩和归档,在Linux上, targzip 是首选。

同步文件和目录

维护系统备份副本的常见策略包括保持一个或多个目录与位于本地系统(通常是某种可移动存储设备)或远程系统上的另一个(或多个)目录同步。例如,我们可能有一个正在开发的网站的本地副本,并不时地将其与远程web服务器上的“实时”副本同步。

在类Unix环境中,执行此任务的首选工具是 rsync 。此程序可以通过使用 rsync remote-update protocol 同步本地和远程目录,该协议允许 rsync 快速检测两个目录之间的差异,并执行使它们同步所需的最小复制量。与其他类型的复制程序相比,这使得 rsync 的使用非常快速和经济。

rsync 的调用方式如下:

rsync options source destination

其中源(source)和目的地(destination)是以下之一:

请注意,源文件或目标文件必须是本地文件。不支持远程到远程复制

让我们在一些本地文件上尝试 rsync 。首先,让我们清理一下 foo 目录。

接下来,我们将把 playground目录与 foo 中的相应副本同步。

我们包含了 -a 选项(用于存档——导致递归和文件属性的保留)和 -v 选项(详细输出),以在 foo 中创建 playground 目录的镜像。当命令运行时,我们将看到正在复制的文件和目录的列表。最后,我们将看到这样的摘要消息,指示执行的复制量:

如果我们再次运行该命令,我们将看到不同的结果。

请注意,没有文件列表。这是因为 rsync 检测到 ~/playground~/foo/playground 之间没有差异,因此不需要复制任何内容。如果我们在 playground 中修改一个文件并再次运行 rsync

我们看到 rsync 检测到了更改,并仅复制了更新的文件。

当我们指定 rsync 源时,我们可以使用一个微妙但有用的功能。让我们考虑两个目录。

source 目录中包含一个名为 file1 的文件, destination 目录为空。如果我们像这样执行 sourcedestination 的复制:

然后 rsync 将目录 source 复制到 destination

但是,如果我们在源目录名后附加一个 /rsync 将只复制源目录的内容,而不会复制目录本身。

如果我们只想复制目录的内容,而不想在目标中创建另一级目录,这很方便。我们可以将其视为其结果类似于 source/* ,但此方法将复制源目录的所有内容,包括隐藏文件。

作为一个实际例子,让我们考虑一下我们之前在 tar 中使用的虚拟外部硬盘。如果我们将驱动器连接到系统,并再次将其挂载到 /media/BigDisk ,我们可以通过首先在外部驱动器上创建一个名为 /backup 的目录,然后使用 rsync 将最重要的内容从系统复制到外部驱动器来执行有用的系统备份。

在这个例子中,我们将 /etc/home/usr/local 目录从系统复制到我们想象中的存储设备。我们包含了 --delete 选项,用于删除备份设备上可能存在但源设备上已不存在的文件(这在我们第一次进行备份时无关紧要,但在后续副本上很有用)。重复连接外部驱动器并运行此 rsync 命令的过程将是保持小型系统备份的一种有用(尽管不是理想)的方法。当然,别名在这里也会有所帮助。我们可以创建一个别名并将其添加到 .bashrc 文件中以提供此功能。

现在,我们所要做的就是连接外部驱动器并运行 backup 命令来完成这项工作。

在网络上使用rsync

rsync 的真正优点之一是它可以用于通过网络复制文件。毕竟, rsync 中的 r 代表“远程”。远程复制可以通过以下两种方式之一完成。

第一种方法是使用安装了 rsync 的另一个系统,以及 ssh 等远程shell程序。假设我们的本地网络上有另一个系统,有很多可用的硬盘空间,我们想使用远程系统而不是外部驱动器来执行备份操作。假设它已经有一个名为 /backup 的目录,我们可以在其中传递文件,我们可以这样做:

我们对命令进行了两次更改,以方便网络复制。首先,我们添加了 --rsh=ssh 选项,该选项指示 rsyncssh 程序用作其远程shell。

通过这种方式,我们能够使用 ssh 加密的隧道将数据从本地系统安全地传输到远程主机。其次,我们通过在目标路径名前加上远程主机的名称(在本例中,远程主机名为 remote-sys )来指定远程主机。

rsync 可用于通过网络同步文件的第二种方式是使用 rsync 服务器。 rsync 可以配置为作为守护进程运行,并监听传入的同步请求。这通常是为了允许对远程系统进行镜像。例如,Red Hat Software为其Fedora发行版维护了一个正在开发的大型软件包存储库。软件测试人员在发行版发布周期的测试阶段镜像此集合非常有用。由于存储库中的文件经常更改(通常每天不止一次),因此最好通过定期同步来维护本地镜像,而不是批量复制存储库。其中一个存储库保存在杜克大学;我们可以使用 rsync 的本地副本及其 rsync 服务器进行镜像,如下所示:

在这个例子中,我们使用远程 rsync 服务器的URI,它由一个协议( rsync:// )组成,后面是远程主机名( archive.linux.duke.edu ),后面是存储库的路径名。

总结

我们研究了Linux和其他类Unix操作系统上使用的常见压缩和归档程序。对于归档文件, tar/gzip 组合是类Unix系统上的首选方法,而 zip/uzip 用于与Windows系统的互操作性。最后,我们看了 rsync 程序(个人最喜欢的程序),它对于跨系统高效同步文件和目录非常方便。