IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope: Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  Linux  >

学习 Linux,101: 文件和目录管理

熟悉 Linux 文件和目录

developerWorks
文档选项

未显示需要 JavaScript 的文档选项

讨论

英文原文

英文原文


级别: 中级

Ian Shields, 高级程序员, IBM

2009 年 11 月 02 日

您可能已经听说过 “Linux 中的每项内容都可以看作一个文件”,那么就让我们通过牢固地掌握文件和目录管理内容来开始我们的 Linux 学习吧 —— 寻找、列出、移动、复制和归档。您可以利用本文的内容来准备 Linux 系统管理员认证 LPI® 101 考试,或者仅仅是从中获取乐趣。
关于本系列

本系列文章帮助您了解 Linux 系统管理任务。您还可以利用本系列内容来准备 Linux Professional Institute Certification 级别 1 (LPIC-1) 考试

查看我们的 学习 Linux,101:LPIC-1 路线图,获得本系列所有文章的描述和链接。该路线图目前仍然在进行中,反映了 LPIC-1 考试的最新目标(2009 年 4 月):一旦文章完成之后,我们将它们添加到路线图中。但是,与此同时,您可以在我们的 LPI 认证考试准备教程 中找到类似内容的早期版本,这些内容支持 2009 年 4 月以前发布的 LPIC-1 目标。

先决条件

要充分地利用本系列文章,您应当对 Linux 以及在其中运行本文介绍的命令的 Linux 系统有一个基本的了解。某些情况下,程序的不同版本可能使用不同的格式化输出,因此您的结果可能并不总是与本文所示的清单和图形完全一样。

概述

本文将帮助您掌握有关文件和目录管理的基本 Linux 命令。您将学习:

  • 列出目录内容
  • 复制、移动或删除文件和目录
  • 重复操作多个文件和目录
  • 使用通配符模式操作文件
  • 使用 find 命令,根据类型、大小或时间查找和操作文件
  • 使用 gzipbzip2 压缩和解压缩文件
  • 使用 tarcpiodd 归档文件

本文帮助您准备 Linux Professional Institute Junior Level Administration (LPIC-1) 考试 101 中主题 103 下的目标 103.2。该目标的权值为 4。





回页首


列出目录

Linux 和 UNIX® 系统中的所有文件都可以作为一个大型树型文件系统的一部分访问,这个树型文件系统的根为 /。通过挂载 分支可以将它们添加到树中,通过解除挂载 可以移除它们。挂载和解除挂载的内容将在挂载和解除挂载文件系统 一文介绍。 (参见 学习 Linux,101:LPIC-1 路线图)。

列出目录条目

在本文中,我们将使用 “学习 Linux,101:文本流和过滤器” 一文中创建的文件来练习命令。如果您完成了上篇文章的练习,那么您应该在您的主目录中创建了一个目录 lpi103-2。如果还没有的话,那么可以使用系统中的另一个目录来练习本文讨论的命令。

文件和目录名可以是绝对 的,这表示名称以 / 开头,也可以相对当前工作目录,这表示不是以 / 开头。文件或目录的绝对路径的组成为:在 0 个或多个目录名后附加一个 /,其中每个目录名的后面都有一个 /,然后是一个最终文件名。

至于相对于当前工作目录的文件或目录名,只需要将工作目录的绝对名、/ 和相对名连接在一起。例如,我们在早期文章中在我的主目录 /home/ian 中创建的目录 lpi103-2,它的完整(即绝对)路径为 /home/ian/lpi103-2。

您可以使用 pwd 命令显示当前工作目录的名称。此命令通常也可以用于 PWD 环境变量。清单 1 展示了 pwd 命令的使用,以及通过三种不同的方法使用 ls 命令列出此目录中的文件。


清单 1. 列出目录条目
				
[ian@echidna lpi103-2]$ pwd
/home/ian/lpi103-2
[ian@echidna lpi103-2]$ echo "$PWD"
/home/ian/lpi103-2
[ian@echidna lpi103-2]$ ls
sedtab  text1  text2  text3  text4  text5  text6  xaa  xab  yaa  yab
[ian@echidna lpi103-2]$ ls "$PWD"
sedtab  text1  text2  text3  text4  text5  text6  xaa  xab  yaa  yab
[ian@echidna lpi103-2]$ ls /home/ian/lpi103-2
sedtab  text1  text2  text3  text4  text5  text6  xaa  xab  yaa  yab

可以看到,您可以将一个相对或绝对目录名作为 ls 目录的参数,它将列出该目录中的内容。

列出详细内容

在一台存储设备中,文件或目录被包含到一个块(block)组合中。有关文件的信息被包含在一个索引节点(inode)中,其中记录如下信息:所有者、最后一次访问文件的时间、文件大小、是否为目录以及谁可以读取或写入数据。inode 编号也被称为文件序列号(file serial number),并且在一个特定文件系统中是唯一的。我们可以使用 -l(或 --format=long)选项来显示存储在 inode 中的某些信息。

默认情况下,ls 命令不会列出特殊文件,这些文件的文件名以点号 (.) 开头。除根目录外的所有目录都至少包含两个特殊条目:目录本身 (.) 和父目录 (..)。根目录没有父目录。

清单 2 使用 -l-a 选项显示所有文件的长格式的列表,包括 . 和 .. 目录条目。


清单 2. 显示一个长目录列表
				
[ian@echidna lpi103-2]$ ls -al
total 52
drwxrwxr-x.  2 ian ian 4096 2009-08-11 21:21 .
drwx------. 35 ian ian 4096 2009-08-12 10:55 ..
-rw-rw-r--.  1 ian ian    8 2009-08-11 21:17 sedtab
-rw-rw-r--.  1 ian ian   24 2009-08-11 14:02 text1
-rw-rw-r--.  1 ian ian   25 2009-08-11 14:27 text2
-rw-rw-r--.  1 ian ian   63 2009-08-11 15:41 text3
-rw-rw-r--.  1 ian ian   26 2009-08-11 15:42 text4
-rw-rw-r--.  1 ian ian   24 2009-08-11 18:47 text5
-rw-rw-r--.  1 ian ian   98 2009-08-11 21:21 text6
-rw-rw-r--.  1 ian ian   15 2009-08-11 14:41 xaa
-rw-rw-r--.  1 ian ian    9 2009-08-11 14:41 xab
-rw-rw-r--.  1 ian ian   17 2009-08-11 14:41 yaa
-rw-rw-r--.  1 ian ian    8 2009-08-11 14:41 yab

在清单 2 中,第一行显示所列文件使用的磁盘块的总数(52)。其余行列出了目录的条目。

  • 第一个字段(本例中为 drwxrwxr-x 或 -rw-rw-r--)告诉我们,文件是一个目录 (d) 还是一个普通文件 (-)。对于特殊文件,还会看到符号链接 (l) 或其他值(例如 /dev 文件系统中的文件)。您将在创建和修改硬链接和符号链接 一文(参见 学习 Linux,101:LPIC-1 路线图)中了解到有关符号链接的更多内容。类型之后是针对所有者、所有者所在组的成员、每一个成员的三组特权。这三个值分别表示用户、组、组成员是否拥有读 (r)、写 (w) 或 (x) 执行权限。诸如 setuid 之类的用户将在管理文件权限和所有权(参见 学习 Linux,101:LPIC-1 路线图)一文中介绍。
  • 下一个字段是一个数字,告诉我们文件的硬链接 的数量。我们已经介绍过,inode 包含有关文件的信息。文件的目录条目包含到文件的 inode 的硬链接(或指针),因此列出的每个条目都应该至少拥有一个硬链接。目录条目对 . 条目和每个子目录条目使用另外的硬链接。因此我们可以从清单 2 中可以看到,使用 .. 表示的主目录有大量子目录,因此包含 35 个硬链接。
  • 接下来两个字段分别为文件的所有者和所有者的主组。某些系统,例如 Red Hat 或 Fedora 系统,在默认情况下为每个用户提供单独的组。在其他系统中,所有用户可能位于一个或多个组中。
  • 下一个字段包含文件的长度,以字节为单位。
  • 倒数第二个字段包含最后一次修改的时间戳。
  • 最后一个字段包含文件或目录的名称。

ls 命令的 -i 选项将显示 inode 号。您将在本文后面以及 创建和修改硬链接和符号链接(参见 学习 Linux,101:LPIC-1 路线图)中再次见到有关 inode 的介绍。

多个文件

您还可以为 ls 命令指定多个参数,其中的每个名称都可能是文件或目录的名称。对于目录名,ls 命令将列出目录的内容,而不是关于目录本身的信息。在我们的示例中,假设当在父目录中列出目录时,我们希望获得有关 lpi103-2 目录条目本身的信息。命令 ls -l ../lpi103-2 将提供类似前例的列表。清单 3 将展示如何添加 -d 选项以列出有关目录条目的信息,而不是目录的内容,以及如何列出多个文件或目录的条目。


清单 3. 使用 ls -d
				
[ian@echidna lpi103-2]$ ls -ld ../lpi103-2 sedtab xaa
drwxrwxr-x. 2 ian ian 4096 2009-08-12 15:31 ../lpi103-2
-rw-rw-r--. 1 ian ian    8 2009-08-11 21:17 sedtab
-rw-rw-r--. 1 ian ian   15 2009-08-11 14:41 xaa

注意,lpi103-2 的修改时间不同于前一个列表中的修改时间。同样,和前一个列表相同,它与该目录中的任何文件的时间戳都不同。这是否就是您所期望的?并不是这样。然而,在撰写本文时,我创建了一些额外的的例子并删除了它们,因此目录时间戳反映了这一更改。稍后在 处理多个文件和目录 中,我们将更详细地讨论文件时间。

对输出排序

默认情况下,ls 将按字母顺序列出文件。可以使用多种选项对输出进行排序。例如,ls -t 将按照修改时间排序(从最新到最旧),而 ls -lS 将生成一个按大小排序的长列表(从最大到最小)。添加 -r 将反向排序。例如,使用 ls -lrt 生成一个按从最旧到最新排序的长列表。参考手册页面,了解有关排列文件和目录的其他方式。





回页首


复制、移动和删除文件

我们现在已经了解了一些创建文件的方法,但是假设我们希望复制文件、重命名文件、在文件系统层级结构中移动文件,甚至删除它们。我们使用三个简短的命令来实现这些目的。

cp
用于复制一个或多个文件或目录。您必须提供一个(或多个) 名和一个目标 名。源名或目标名可能包含一个路径说明。如果目标是一个现有目录,那么所有源将被复制目标中。如果目录是一个不存在的目录,那么(单一)源也必须为一个目录,并且源目录的副本及其内容使用目标名作为新名称。如果目标是一个文件,那么(单一)源必须也为文件,而源文件的副本使用目标名作为新名,替换任何现有的具有相同名称的文件。注意,在 DOS 和 Windows 操作系统中,不会做出目标为当前目录的默认假设。
mv
用于移动重命名 一个或多个文件或目录。一般来说,您使用的名称将遵守与 cp 相同的规则;您可以重命名某个文件或将一组文件移动到一个新目录中。由于名称只是一个链接到某个 inode 的目录条目,因此 inode 号只有在文件被移动到另一个文件系统才会发生更改就不足为怪了,在这种情况下,移动文件看上去就类似于在复制文件之后删除它。
rm
用于删除 一个或多个文件。我们后面将介绍如何删除目录。
rename 命令在哪里?

如果您熟悉 DOS 或 Windows® 系统,您会发现使用 mv 重命名文件有点怪异。Linux 确实提供了 rename 命令,但是该命令与 DOS 和 Windows 下相同名称的命令具有不同的语法。查看手册页,了解该命令的使用详情。

清单 4 演示了 cpmv 的使用,它们对我们的文本文件执行了一些备份复制。我们使用 ls -i 展示其中一些文件的 inode。

  1. 我们首先为 text1 文件生成一个副本 text1.bkp。
  2. 然后决定使用 mkdir 命令创建一个备份子目录
  3. 我们为文本 1 生成第二个备份副本,这一次是在备份目录中,并显示出所有三个文件都具有不同的 inode。
  4. 随后将 text1.bkp 移动到备份目录中,然后将其重命名,使其与第二个备份更加一致。我们本来可以使用一个单个命令完成这些操作,但是为了演示的目的,我们在这里使用了两个命令。
  5. 我们再次检查 inode,然后确定 inode 为 934193 的 text1.bkp 不再存在于 lpi103-2 目录,但是该 inode 仍然为备份目录中的 text1.bkp.1 保留下来。

清单 4. 复制和移动文件
				
[ian@echidna lpi103-2]$ cp text1 text1.bkp
[ian@echidna lpi103-2]$ mkdir backup
[ian@echidna lpi103-2]$ cp text1 backup/text1.bkp.2
[ian@echidna lpi103-2]$ ls -i text1 text1.bkp backup
933892 text1  934193 text1.bkp

backup:
934195 text1.bkp.2
[ian@echidna lpi103-2]$ mv text1.bkp backup
[ian@echidna lpi103-2]$ mv backup/text1.bkp backup/text1.bkp.1
[ian@echidna lpi103-2]$ ls -i text1 text1.bkp backup
ls: cannot access text1.bkp: No such file or directory
933892 text1

backup:
934193 text1.bkp.1  934195 text1.bkp.2

一般来说,cp 将在现有副本上复制文件,如果现有文件可写的话。另一方面,如果目标存在,mv 不会移动或重命名文件。有一些有用的选项与 cpmv 的这种行为有关。

-f--force
将促使 cp 尝试阐释一个现有目标文件,即使它不是可写的
-i--interactive
将要求在尝试替换某个现有文件之前进行确认
-b--backup
将为即将被替换的任何文件生成备份

和前面一样,您需要参考手册页来获得这些和其他复制和移动选项的详细内容。

清单 5 演示了备份复制和文件删除。


清单 5. 生成备份副本并删除文件
				
[ian@echidna lpi103-2]$ cp text2 backup
[ian@echidna lpi103-2]$ cp --backup=t text2 backup
[ian@echidna lpi103-2]$ ls backup
text1.bkp.1  text1.bkp.2  text2  text2.~1~
[ian@echidna lpi103-2]$ rm backup/text2 backup/text2.~1~
[ian@echidna lpi103-2]$ ls backup
text1.bkp.1  text1.bkp.2

注意,rm 命令还接受 -i(交互式)和 -f(强制选项)。当您使用 rm 删除文件后,文件系统将不再访问它。某些系统在默认情况下为根用户设置一个别名 alias rm='rm -i',以防止出现意外的文件删除。如果您担心会不小心删除文件的话,这对于普通用户来说也是一个好主意。

在结束这些内容的讨论之前,应当注意 cp 命令在默认情况下会为新的文件创建一个新的时间戳。所有者和组均被设置为执行复制的用户的所有者和组。-p 选项可能被用于保存选择的属性。注意,根用户可能为可以保留所有权的唯一用户。参考手册页获得详情。





回页首


创建和删除目录

我们已经了解了如何使用 mkdir 创建目录。现在我们将进一步查看 mkdir 并介绍 rmdir,后者用于删除目录。

Mkdir

假设我们希望在 lpi103-2 目录中创建子目录 dir1 和 dir2。和前面介绍过的其他命令一样,mkdir 可以一次处理多个目录创建请求,如清单 6 所示。


清单 6. 创建多个目录
				
[ian@echidna lpi103-2]$ mkdir dir1 dir2
			

注意,在成功完成后不会产生输出,但是您可以使用 echo $? 来确认退出代码确实为 0。

相反,如果您希望创建一个嵌入式的子目录,比如 d1/d2/d3,那么命令将会失败,因为 d1 和 d2 目录并不存在。幸运的是,mkdir 具有一个 -p 选项,它允许创建任何所需的父目录,如清单 7 所示。


清单 7. 创建父目录
				
[ian@echidna lpi103-2]$ mkdir d1/d2/d3
mkdir: cannot create directory `d1/d2/d3': No such file or directory
[ian@echidna lpi103-2]$ echo $?
1
[ian@echidna lpi103-2]$ mkdir -p d1/d2/d3
[ian@echidna lpi103-2]$ echo $?
0

Rmdir

使用 rmdir 命令删除目录正好与创建过程相反。同样,可以用 -p 选项来删除父目录。只有在目录为空的情况下才可以使用 rmdir 删除目录,因为不存在可以强制删除的选项。我们将在讨论 递归操作 时查看另一种可以完成这一特殊任务的方法。了解了这种方法后,您将很少会在命令行中使用 rmdir,但是了解该命令仍然是有用的。

为了解释目录删除,我们将 text1 文件复制到目录 d1/d2 中,这样它就不会成为空目录。我们随后使用 rmdir 来删除刚刚用 mkdir 创建的所有目录。可以看到,d1 和 d2 没有被删除,因为 d2 不为空。另一个目录则被删除,当我们从 d2 删除 text1 的副本时,我们只需要调用 rmdir -p 即可删除 d1 和 d2。


清单 8. 删除目录
				
[ian@echidna lpi103-2]$ cp text1 d1/d2
[ian@echidna lpi103-2]$ rmdir -p d1/d2/d3 dir1 dir2
rmdir: failed to remove directory `d1/d2': Directory not empty
[ian@echidna lpi103-2]$ ls . d1/d2
.:
backup  sedtab  text2  text4  text6  xab  yab
d1      text1   text3  text5  xaa    yaa

d1/d2:
text1
[ian@echidna lpi103-2]$ rm d1/d2/text1
[ian@echidna lpi103-2]$ rmdir -p d1/d2
			





回页首


处理多个文件和目录

到目前为止,我们使用的命令已经处理了一个单个文件,或者一些个别命名的文件。在本文的其余部分中,我们将查看处理多个文件的各种操作,递归式处理文件树的某一部分,保存或恢复多个文件或目录。





回页首


递归式操作

递归式列表

ls 命令有一个 -R(注意为大写 “R”)选项,可以列出一个目录及其所有子目录。递归式操作只能应用于目录名;它不会在目录树中查找名为 ‘text1’ 之类的文件。您可以将 -R 与已经介绍过的其他选项结合使用。lpi103-2 目录的递归式列表包括 inode 号,如清单 9 所示。


清单 9. 递归式显示目录列表
				
[ian@echidna lpi103-2]$ ls -iR
.:
934194 backup  933892 text1  933898 text3  933900 text5  933894 xaa  933896 yaa
933901 sedtab  933893 text2  933899 text4  933902 text6  933895 xab  933897 yab

./backup:
934193 text1.bkp.1  934195 text1.bkp.2

递归式复制

可以使用 -r(或 -R--recursive)选项来使 cp 命令进入到源目录并以递归的方式复制目录。为了防止出现无穷递归,源目录本身可能不会被复制。清单 10 展示了如何将 lpi103-2 目录中的所有内容复制到 copy1 子目录。我们使用 ls -R 展示生成的目录树。


清单 10. 递归复制
				
[ian@echidna lpi103-2]$ cp -pR . copy1
cp: cannot copy a directory, `.', into itself, `copy1'
[ian@echidna lpi103-2]$ ls -R
.:
backup  copy1  sedtab  text1  text2  text3  text4  text5  text6  xaa  xab  yaa  yab

./backup:
text1.bkp.1  text1.bkp.2

./copy1:
text2  text3  text5  xaa  yaa  yab

递归删除

我们前面提到,rmdir 只能删除空目录。我们可以使用 -r(或 -R--recursive)选项来使 rm 命令同时删除文件目录,如清单 11 所示,我们将删除刚刚创建的 copy1 目录和它包含的内容,包括备份子目录及其内容。


清单 11. 递归式删除
				
[ian@echidna lpi103-2]$ rm -r copy1
[ian@echidna lpi103-2]$ ls -R
.:
backup  sedtab  text1  text2  text3  text4  text5  text6  xaa  xab  yaa  yab

./backup:
text1.bkp.1  text1.bkp.2

如果您具有不可写的文件,那么可能需要添加 -f 选项来强制删除。这通常由根用户在清理系统时执行,但是会发出警告,因为您有可能会不小心删除重要的数据。





回页首


通配符和 globbing

通常,您需要对多个文件系统对象执行单一操作,而不需要像前面的递归操作一样对整个树进行操作。例如,您可能想要找出在 lpi103-2 中创建的所有文本文件的修改时间,而不需要列出分散的文件。尽管这很容易在小目录中实现,但是对于大型文件系统则非常困难。

要解决这个问题,可以使用 bash shell 中内置的通配符支持。这种支持也称为 “globbing”(因为它最初被实现为一个名为 /etc/glob 的程序),让您能够使用通配符模式指定多个文件。

包含任何 '?'、'*' 或 '[' 字符的字符串就是一个通配符模式。Globbing 是指 shell(或另一个程序)将这些模式扩展为一组匹配该模式的参数的过程。这种匹配按照如下方式完成:

?
匹配任何单个字读。
*
匹配任何字符串,包括空字符串。
[
引入了一个字符类(character class)。字符类是一个非空字符串,以 ']' 结尾。匹配意味着需要与方括号中包括的任何单个字符相一致。这里需要考虑一些特殊的事项。
  • '*' 和 '?' 字符与它们自身匹配。如果在文件名中使用这些字符,那么需要注意适当的引用或转义。
  • 由于字符串必须是非空的并以 ']' 终止,如果您需要匹配字符串的话,您必须将 ']' 放到字符串的首位
  • 两个字符之间的 '-' 字符表示一个范围,包括这两个字符和排序序列中介于这两个字符之间的所有字符。例如,[0-9a-fA-F] 表示任何大写或小写十六进制数位。您可以通过将 '-' 放到一个范围的首位或末位来匹配它。
  • 如果范围的首个字符为 '!' 字符,那么它将对范围求余,即它将匹配剩余字符以外的所有字符。例如,[!0-9] 表示除 0 到 9 之间数字的任何字符。将 '!' 放在首位以外的任意位置都可以匹配它本身。注意 '!' 也可以用于 shell history 函数,因此需要小心地对它进行适当的转义。

注意:通配符模式和常规表达式模式具有一些共同点,但是它们是同的。需要仔细留意。

Globbing 被单独应用到路径名的每个组成中。您无法匹配 '/',也不能把它包含在一个范围中。您可以在指定多个文件或目录名时使用它,例如在 lscpmvrm 命令中。在清单 12 中,我们首先创建一对名字奇怪的文件,然后对通配符模式使用 lsrm 命令。


清单 12. 通配符模式示例
				
[ian@echidna lpi103-2]$ echo odd1>'text[*?!1]'
[ian@echidna lpi103-2]$ echo odd2>'text[2*?!]'
[ian@echidna lpi103-2]$ ls
backup  text1       text2       text3  text5  xaa  yaa
sedtab  text[*?!1]  text[2*?!]  text4  text6  xab  yab
[ian@echidna lpi103-2]$ ls text[2-4]
text2  text3  text4
[ian@echidna lpi103-2]$ ls text[!2-4]
text1  text5  text6
[ian@echidna lpi103-2]$ ls text*[2-4]*
text2  text[2*?!]  text3  text4
[ian@echidna lpi103-2]$ ls text*[!2-4]* # Surprise!
text1  text[*?!1]  text[2*?!]  text5  text6
[ian@echidna lpi103-2]$ ls text*[!2-4] # Another surprise!
text1  text[*?!1]  text[2*?!]  text5  text6
[ian@echidna lpi103-2]$ echo text*>text10
[ian@echidna lpi103-2]$ ls *\!*
text[*?!1]  text[2*?!]
[ian@echidna lpi103-2]$ ls *[x\!]*
text1  text[*?!1]  text10  text2  text[2*?!]  text3  text4  text5  text6  xaa  xab
[ian@echidna lpi103-2]$ ls *[y\!]*
text[*?!1]  text[2*?!]  yaa  yab
[ian@echidna lpi103-2]$ ls tex?[[]*
text[*?!1]  text[2*?!]
[ian@echidna lpi103-2]$ rm tex?[[]*
[ian@echidna lpi103-2]$ ls *b*
sedtab  xab  yab

backup:
text1.bkp.1  text1.bkp.2
[ian@echidna lpi103-2]$ ls backup/*2
backup/text1.bkp.2
[ian@echidna lpi103-2]$ ls -d .*
.  ..

注意:

  1. 结合使用 '*' 会出现一些意外。模式 '*[!2-4]' 匹配名称中不包含 2、3 或 4 的最长的一部分,这部分可以同时被 text[*?!1] 和 text[2*?!] 匹配。现在所有这些意外都应该清楚了。
  2. 和前面的 ls 示例一样,如果模式扩展导致一个目录名,并且没有指定 -d 选项,那么该目录的内容将被列出(和前例中的模式 '*b*' 一样)。
  3. 如果一个文件名以句点 (.) 开头,那么该字符必须被明确匹配。注意,只有最后一个 ls 命令列出两个特殊的目录条目(. 和 ..)。

请注意,命令中的任何通配符都可以被 shell 扩展,这将导致意外的结果。并且,如果您指定一个不匹配任何文件系统对象的模式,那么 POSIX 要求原始模式字符串被传递给命令。一些早期的实现将一个 null 列表传递给命令,因此您可能会遇到一些表现出异常行为的老脚本。我们将在清单 13 中解释这些内容。


清单 13. 通配符模式异常
				
[ian@echidna lpi103-2]$ echo text*
text1 text10 text2 text3 text4 text5 text6
[ian@echidna lpi103-2]$ echo "text*"
text*
[ian@echidna lpi103-2]$ echo text[[\!?]z??
text[[!?]z??

有关 globbing 的更多信息,请查阅 man 7 glob。您将需要章节号,因为第 3 节中也介绍了 glob 信息。理解所有不同 shell 交互的最佳方式是进行实践,因此您需要多多尝试这些通配符。注意,在使用 cpmvrm 出现异常行为之前,使用 ls 检查您的通配符模式。





回页首


对文件执行 Touch 命令

我们现在来看看 touch 命令,它将更新文件访问和修改时间或创建空文件。在下一部分中,我们将探讨如何使用这些信息查找文件和目录。我们将继续在示例中使用 lpi103-2 目录。我们还将查看各种指定时间戳的方法。

touch

不包含任何选项的 touch 命令使用一个或多个文件名作为参数,并将更新文件的修改时间。这个时间戳通常会和一个长目录列表一同显示。在清单 14 中,我们使用 echo 创建了一个小文件 f1,然后使用一个长目录列表来显示修改时间(即 mtime)。在本例中,修改时间正好是文件的创建时间。我们随后使用 sleep 命令来等待 60 秒,并在此运行 ls。注意,该文件的时间戳已经修改了一段时间。


清单 14. 使用 touch 更新修改时间
				
[ian@echidna lpi103-2]$ echo xxx>f1; ls -l f1; sleep 60; touch f1; ls -l f1
-rw-rw-r--. 1 ian ian 4 2009-08-14 18:24 f1
-rw-rw-r--. 1 ian ian 4 2009-08-14 18:25 f1

如果为一个并不存在的文件指定文件名,那么 touch 通常会为您创建一个空文件,除非您指定了 -c--no-create 选项。清单 15 展示了这两个命令。注意,只有 f2 被创建。


清单 15. 使用 touch 创建空文件
				
[ian@echidna lpi103-2]$ touch f2; touch -c f3; ls -l f*
-rw-rw-r--. 1 ian ian 4 2009-08-14 18:25 f1
-rw-rw-r--. 1 ian ian 0 2009-08-14 18:27 f2

touch 命令还可以将文件的修改时间(也称为 mtime)设置为一个特定日期和时间,可以使用 -d-t 选项。-d 可以非常灵活地处理它将接受的日期和时间格式,而 -t 选项需要至少一个 MMDDhhmm 时间,年份和秒数是可选的。清单 16 展示了一些例子。


清单 16. 使用 touch 设置 mtime
				
[ian@echidna lpi103-2]$ touch -t 200908121510.59 f3
[ian@echidna lpi103-2]$ touch -d 11am f4
[ian@echidna lpi103-2]$ touch -d "last fortnight" f5
[ian@echidna lpi103-2]$ touch -d "yesterday 6am" f6
[ian@echidna lpi103-2]$ touch -d "2 days ago 12:00" f7
[ian@echidna lpi103-2]$ touch -d "tomorrow 02:00" f8
[ian@echidna lpi103-2]$ touch -d "5 Nov" f9
[ian@echidna lpi103-2]$ ls -lrt f*
-rw-rw-r--. 1 ian ian 0 2009-07-31 18:31 f5
-rw-rw-r--. 1 ian ian 0 2009-08-12 12:00 f7
-rw-rw-r--. 1 ian ian 0 2009-08-12 15:10 f3
-rw-rw-r--. 1 ian ian 0 2009-08-13 06:00 f6
-rw-rw-r--. 1 ian ian 0 2009-08-14 11:00 f4
-rw-rw-r--. 1 ian ian 4 2009-08-14 18:25 f1
-rw-rw-r--. 1 ian ian 0 2009-08-14 18:27 f2
-rw-rw-r--. 1 ian ian 0 2009-08-15 02:00 f8
-rw-rw-r--. 1 ian ian 0 2009-11-05 00:00 f9

如果您不确定某个日期表达式所表示的日期,那么可以使用 date 命令确定日期。它还接受 -d 选项并解析与 touch 相同的日历格式。

可以使用 -r(或 --reference)选项以及一个引用文件名 来表示 touch(或 date)应当使用现有文件的时间戳。清单 17 给出了一些示例。


清单 17. 引用文件的时间戳
				
[ian@echidna lpi103-2]$ date
Fri Aug 14 18:33:48 EDT 2009
[ian@echidna lpi103-2]$ date -r f1
Fri Aug 14 18:25:50 EDT 2009
[ian@echidna lpi103-2]$ touch -r f1 f1a
[ian@echidna lpi103-2]$ ls -l f1*
-rw-rw-r--. 1 ian ian 4 2009-08-14 18:25 f1
-rw-rw-r--. 1 ian ian 0 2009-08-14 18:25 f1a

Linux 系统同时记录文件修改 时间和文件访问 时间。这两个时间也分别被称为 mtimeatime。当文件被创建时,这两个时间戳均被设置为相同的值,在文件被修改时,两个值同时被重置。如果文件被访问过,那么访问时间将被更新,即使文件未被修改。对于我们的最后一个 touch 例子,我们将查看文件访问 时间。-a(或 --time=atime--time=access--time=use)选项表示访问时间应该被更新。清单 18 使用 cat 命令访问 f1 文件并显示其内容。我们随后使用 ls -lls -lu 分别显示 f1 和 f1a 的修改和访问时间,f1a 是使用 f1 作为引用文件创建的。我们随后使用 touch -a 将 f1 的访问时间重置为 f1a 的访问时间,然后检验它是否被重置。


清单 18. 访问时间和修改时间
				
[ian@echidna lpi103-2]$ ls -lu f1*
-rw-rw-r--. 1 ian ian 4 2009-08-14 18:39 f1
-rw-rw-r--. 1 ian ian 0 2009-08-14 18:25 f1a
[ian@echidna lpi103-2]$ ls -l f1*
-rw-rw-r--. 1 ian ian 4 2009-08-14 18:25 f1
-rw-rw-r--. 1 ian ian 0 2009-08-14 18:25 f1a
[ian@echidna lpi103-2]$ touch -a -r f1a f1
[ian@echidna lpi103-2]$ ls -lu f1*
-rw-rw-r--. 1 ian ian 4 2009-08-14 18:25 f1
-rw-rw-r--. 1 ian ian 0 2009-08-14 18:25 f1a

有关大量可用日期和时间规范的更多完整信息,请参考手册或信息页中有关 touchdate 命令的内容。





回页首


查找文件

现在我们已经介绍了文件和目录主题中的递归和 globbing 内容,递归涉及文件的所有方面,而 globbing 更具有针对性,让我们看看 find 命令,该命令更像是外科医生的手术刀。find 命令用于根据名称、时间戳或大小等条件查找一个或多个目录树中的文件。我们再一次使用 lpi103-2 目录。

find

find 命令将使用全名或部分名称搜索文件或目录,或者按照其他搜索条件搜索,例如大小、类型、文件所有者、创建日期或最近一次的访问时间。最基本的查找是按全名或部分名称查找。清单 19 展示了 lpi103-2 目录中的一个例子,我们将查找文件名中包含 '1' 或 'k' 的所有文件,然后执行一些路径搜索,下面的说明部分将加以解释。


清单 19. 按名称查找文件
				
[ian@echidna lpi103-2]$ find . -name "*[1k]*"
./f1a
./f1
./text10
./backup
./backup/text1.bkp.1
./backup/text1.bkp.2
./text1
[ian@echidna lpi103-2]$ find . -ipath "*ACK*1"
./backup/text1.bkp.1
[ian@echidna lpi103-2]$ find . -ipath "*ACK*/*1"
[

说明:

  1. 您可以使用 shell 通配符模式,比如前面 通配符和 globbing 小节中的模式。
  2. 可以使用 -path 代替 -name 来匹配完全路径而不是基本文件名。在本例中,模式可能跨越路径组件,这与普通通配符匹配不同,后者只匹配路径的某个部分。
  3. 如果希望进行不区分大小写的搜索,如前面的 ipath 所示,那么在搜索字符串或模式的 find 选项前面加一个 'i'。
  4. 如果希望搜索名称以点开头的文件或目录名,比如 .bashrc 或当前目录 (.),那么您必须将一个前导圆点指定为模式的一部分。否则,名称搜索将忽略这些文件或目录。

在上面的第一个例子中,我们查找所有文件和一个目录(./backup)。使用 -type 参数以及一个单字母类型来限制搜索。使用 'f' 表示普通文件,使用 'd' 查找目录,使用 'l' 查找符号链接。参考手册页,获得 find 的其他可能类型。清单 20 展示了只搜索目录的结果(-type d)和文件名(*,在本例中可为任何内容)搜索结果。


清单 20. 按类型搜索文件
				
[ian@echidna lpi103-2]$ find . -type d
.
./backup
[ian@echidna lpi103-2]$ find . -type d -name "*"
.
./backup

注意,-type d 不包含任何形式的名称指定,将显示在其名称中具有一个前导圆点的目录(在本例中只包括当前目录),和通配符 “*” 的作用相同。

我们还可以按照文件大小进行搜索,可以搜索具有指定大小的文件 (n),或者搜索文件大小大于 (+n) 或小于 (-n) 某个给定值的文件。通过使用上限和下限大小,我们可以查找大小在某个给定范围内的文件。默认情况下,find-size 选项假设包含 512 字节的块为一个单位,用 'b' 表示。此外,指定 'c' 表示字节,或指定 'k' 表示千字节。在清单 21 中,我们首先查找大小为 0 的所有文件,然后查找大小为 24 或 25 字节的所有文件。注意,指定 -empty 而不是 -size 0 也将查找空文件。


清单 21. 按大小查找文件
				
[ian@echidna lpi103-2]$ find . -size 0
./f1a
./f6
./f8
./f2
./f3
./f7
./f4
./f9
./f5
[ian@echidna lpi103-2]$ find . -size -26c -size +23c -print
./text2
./text5
./backup/text1.bkp.1
./backup/text1.bkp.2
./text1

清单 21 中的第二个例子引入了 -print 选项,它演示了一个可能会对搜索返回的结果执行的操作。在 bash shell 中,如果没有指定任何操作的话,那么这将是默认操作。在某些系统和 shell 中,需要指定一个操作;否则,不会生成输出。

其他操作包括 -ls(输出文件信息,与 ls -lids 命令的输出差不多)和 -exec(为每个文件执行一个命令)。-exec 必须以分号终止,必须对分号进行转义,以避免 shell 首先解释分号。如果希望在命令中使用返回的文件,还必须指定 {}。记住,花括号对于 shell 也是有意义的,因此必须进行转义(或引用)。清单 22 展示了如何使用 -ls-exec 选项列出文件信息。注意第二个表单没有列出 inode 信息。


清单 22. 查找和处理文件
				
[ian@echidna lpi103-2]$ find . -size -26c -size +23c -ls
933893    4 -rw-rw-r--   1 ian      ian            25 Aug 11 14:27 ./text2
933900    4 -rw-rw-r--   1 ian      ian            24 Aug 11 18:47 ./text5
934193    4 -rw-rw-r--   1 ian      ian            24 Aug 12 15:36 ./backup/text1.bkp.1
934195    4 -rw-rw-r--   1 ian      ian            24 Aug 12 15:36 ./backup/text1.bkp.2
933892    4 -rw-rw-r--   1 ian      ian            24 Aug 11 14:02 ./text1
[ian@echidna lpi103-2]$ find . -size -26c -size +23c -exec ls -l '{}' \;
-rw-rw-r--. 1 ian ian 25 2009-08-11 14:27 ./text2
-rw-rw-r--. 1 ian ian 24 2009-08-11 18:47 ./text5
-rw-rw-r--. 1 ian ian 24 2009-08-12 15:36 ./backup/text1.bkp.1
-rw-rw-r--. 1 ian ian 24 2009-08-12 15:36 ./backup/text1.bkp.2
-rw-rw-r--. 1 ian ian 24 2009-08-11 14:02 ./text1

-exec 选项可用于您能够想象到的所有用途。例如:

find . -empty -exec rm '{}' \;

删除目录树中的所有空文件

find . -name "*.htm" -exec mv '{}' '{}l' \;

将所有 .htm 文件重命名为 .html 文件。

在最后一个 find 例子中,我们使用由 touch 命令描述的时间戳来查找具有特定时间戳的文件。清单 23 给出了三个例子:

  1. 当用于 -mtime -2 时,find 命令将查找在最近两天以内修改的所有文件。在本例中,一天即指与当前日期和时间相关的 24 个小时。注意,如果您希望根据访问时间而不是修改时间查找文件,那么将使用 -atime
  2. 添加 -daystart 选项意味着我们希望使用日历计算天数,从午夜开始。现在,f3 已被排除在列表以外。
  3. 最后,我们将展示如何使用以分钟计算的时间范围而不是天数来查找在过去的 1 个小时(60 分钟)至 10 个小时(600 分钟)之间发生修改的文件。

清单 23. 按时间戳查找文件
				
[ian@echidna lpi103-2]$ date
Sat Aug 15 00:27:36 EDT 2009
[ian@echidna lpi103-2]$ find . -mtime -2 -type f -exec ls -l '{}' \;
-rw-rw-r--. 1 ian ian 0 2009-08-14 18:25 ./f1a
-rw-rw-r--. 1 ian ian 4 2009-08-14 18:25 ./f1
-rw-rw-r--. 1 ian ian 0 2009-08-13 06:00 ./f6
-rw-rw-r--. 1 ian ian 0 2009-08-15 02:00 ./f8
-rw-rw-r--. 1 ian ian 0 2009-08-14 18:27 ./f2
-rw-rw-r--. 1 ian ian 58 2009-08-14 17:30 ./text10
-rw-rw-r--. 1 ian ian 0 2009-08-14 11:00 ./f4
-rw-rw-r--. 1 ian ian 0 2009-11-05 00:00 ./f9
[ian@echidna lpi103-2]$ find . -daystart -mtime -2 -type f -exec ls -l '{}' \;
-rw-rw-r--. 1 ian ian 0 2009-08-14 18:25 ./f1a
-rw-rw-r--. 1 ian ian 4 2009-08-14 18:25 ./f1
-rw-rw-r--. 1 ian ian 0 2009-08-15 02:00 ./f8
-rw-rw-r--. 1 ian ian 0 2009-08-14 18:27 ./f2
-rw-rw-r--. 1 ian ian 58 2009-08-14 17:30 ./text10
-rw-rw-r--. 1 ian ian 0 2009-08-14 11:00 ./f4
-rw-rw-r--. 1 ian ian 0 2009-11-05 00:00 ./f9
[ian@echidna lpi103-2]$ find . -mmin -600 -mmin +60 -type f -exec ls -l '{}' \;
-rw-rw-r--. 1 ian ian 0 2009-08-14 18:25 ./f1a
-rw-rw-r--. 1 ian ian 4 2009-08-14 18:25 ./f1
-rw-rw-r--. 1 ian ian 0 2009-08-14 18:27 ./f2
-rw-rw-r--. 1 ian ian 58 2009-08-14 17:30 ./text10

find 命令的手册页可以帮助您了解各种选项,我们无法在这篇简短的介绍中一一介绍它们。





回页首


识别文件

文件名通常具有一个后缀,比如 gif、jpeg 或 html,它将告诉您文件中可能包含的内容。Linux 不需要这种后缀,因此通常不会使用它们来识别文件类型。了解正在处理的文件的类型将使您知道程序将使用什么工具显示或操作文件。file 命令将使您了解到一个或多个文件中的数据的类型。清单 24 展示了使用 file 命令的一些例子。


清单 24. 识别文件内容
				
[ian@echidna lpi103-2]$ file backup text1 f2 ../p-ishields.jpg /bin/echo
backup:            directory
text1:             ASCII text
f2:                empty
../p-ishields.jpg: JPEG image data, JFIF standard 1.02
/bin/echo:         ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically 
linked (uses shared libs), for GNU/Linux 2.6.18, stripped

file 命令尝试使用三种测试来对每个文件进行分类。比如,文件系统测试使用 stat 命令的结果来判断某个文件或目录是否为空。名为 magic 的测试将检查文件中的特定内容,这些内容可以识别文件。这些签名也被称为魔术数字(magic number)。最后,语言测试将查看文本文件中的内容,尝试确定某个文件是 XML 文件、C 或 C++ 语言源文件、troff 文件,或是其他被认为是某些语言处理器的源文件。找到的第一种类型将被生成报告,除非 -k--keep-going 选项被指定。

file 命令具有许多选项,您可以通过手册页获得详细介绍。清单 25 展示了如何使用 -i(或 --mime)选项来将文件类型显示为 MIME 字符串,而不是普通的人类可读输出。


清单 25. 将文件内容识别为 MIME
				
[ian@echidna lpi103-2]$ file -i backup text1 f2 ../p-ishields.jpg /bin/echo
backup:            application/x-directory; charset=binary
text1:             text/plain; charset=us-ascii
f2:                application/x-empty; charset=binary
../p-ishields.jpg: image/jpeg; charset=binary
/bin/echo:         application/x-executable; charset=binary

魔术数字文件也由 file 命令管理。请在此查看手册页获得更多信息。

注意:作为 ImageMagick 包的一部分,identify 命令可以用作额外一种工具,为识别图像文件类型提供更多细节。





回页首


压缩文件

当您备份、归档或传输文件时,通常会对文件进行压缩。在 Linux 环境中,两个流行的压缩程序为 gzipbzip2gzip 命令使用 Lempel-Ziv 算法,而 bzip2 使用 Burrows-Wheeler 块分类算法。

使用 gzip 和 gunzip

压缩通常对文本文件比较有效。许多图像格式的文件已经进行了数据压缩,因此对这些或其他二进制文件进行压缩效果不大。为了演示对较大文本文件执行的压缩,让我们将 /etc/services 复制到我们一直使用的目录中,并使用 gzip 对它执行压缩,如清单 26 所示。我们使用 cp-p 选项保留 /etc/services 的时间戳。注意,压缩后的文件具有相同的时间戳,并且具有一个 .gz 后缀。


清单 26. 使用 gzip 进行压缩
				
[ian@echidna lpi103-2]$ cp -p /etc/services
[ian@echidna lpi103-2]$ ls -l serv*
-rw-r--r--. 1 ian ian 630983 2009-04-10 04:42 services
[ian@echidna lpi103-2]$ gzip services
[ian@echidna lpi103-2]$ ls -l serv*
-rw-r--r--. 1 ian ian 124460 2009-04-10 04:42 services.gz

您使用 gzip-d 选项解压缩使用 gzip 压缩过的文件,更常见的是使用 gunzip 命令。清单 27 展示了第一种方法。注意,未压缩的文件现在具有原始名和时间戳。


清单 27. 使用 gzip 解压缩
				
[ian@echidna lpi103-2]$ gzip -d services.gz
[ian@echidna lpi103-2]$ ls -l serv*
-rw-r--r--. 1 ian ian 630983 2009-04-10 04:42 services

使用 bzip2 和 bunzip2

bzip2 命令以与 gzip 类似的方式运行,如清单 28 所示。


清单 28. 使用 bzip2 进行压缩
				
[ian@echidna lpi103-2]$ ls -l serv*
-rw-r--r--. 1 ian ian 630983 2009-04-10 04:42 services
[ian@echidna lpi103-2]$ bzip2 services
[ian@echidna lpi103-2]$ ls -l serv*
-rw-r--r--. 1 ian ian 113444 2009-04-10 04:42 services.bz2
[ian@echidna lpi103-2]$ bunzip2 services.bz2
[ian@echidna lpi103-2]$ ls -l serv*
-rw-r--r--. 1 ian ian 630983 2009-04-10 04:42 services

gzip 和 bzip2 之间的区别

根据设计,bzip2 的许多选项与 gzip 的选项是相似的,但是这两个命令不包含完全相同的选项。您可能已经注意到,在我们的两个例子中,未压缩的文件具有与原始文件相同的文件名和时间戳。然而,重命名或 touch 已压缩文件会改变这一行为。gzip 命令的 -N--name 选项会强制保留名称和时间戳,但是 bzip2 不会。gzip 命令还有一个 -l 选项,用于显示与已压缩文件有关的信息,包括将在解压缩时使用的名称。清单 29 解释了这两个命令之间的一些区别。


清单 29. gzip 和 bzip2 之间的区别
				
[ian@echidna ~]$ ls -l serv*
-rw-r--r--. 1 ian ian 630983 2009-04-10 04:42 services
[ian@echidna ~]$ gzip -N services
[ian@echidna ~]$ touch services.gz
[ian@echidna ~]$ mv services.gz services-x.gz
[ian@echidna ~]$ ls -l serv*
-rw-r--r--. 1 ian ian 124460 2009-09-23 14:08 services-x.gz
[ian@echidna ~]$ gzip -l services-x.gz
         compressed        uncompressed  ratio uncompressed_name
             124460              630983  80.3% services-x
[ian@echidna ~]$ gzip -lN services-x.gz
         compressed        uncompressed  ratio uncompressed_name
             124460              630983  80.3% services
[ian@echidna ~]$ gunzip -N services-x.gz
[ian@echidna ~]$ ls -l serv*
-rw-r--r--. 1 ian ian 630983 2009-04-10 04:42 services
[ian@echidna ~]$
[ian@echidna ~]$ bzip2 services
[ian@echidna ~]$ mv services.bz2 services-x.bz2
[ian@echidna ~]$ touch services-x.bz2
[ian@echidna ~]$ ls -l serv*
-rw-r--r--. 1 ian ian 113444 2009-09-23 14:10 services-x.bz2
[ian@echidna ~]$ bunzip2 services-x.bz2
[ian@echidna ~]$ ls -l serv*
-rw-rw-r--. 1 ian ian 630983 2009-09-23 14:10 services-x
[ian@echidna ~]$ rm services-x # Don't need this any more
			

gzipbzip2 都接受来自 stdin 的输入。两者都支持用 -c 选项将输出导出到 stdout。

bzip2 还有另外两个相关的命令。

  1. bzcat 命令将文件解压缩到 stdout,其作用等同于 bzip2 -dc
  2. bzip2recover 命令尝试从受损害的 bzip2 文件中恢复数据。

手册页将帮助您了解 gzipbzip2 的其他选项。

其他压缩工具

另外两个更旧一点的程序是 compressuncompress,它们仍然频繁出现在 Linux 和 UNIX 系统中。

此外,来自 Info-ZIP 项目的 zipunzip 命令也在 Linux 中得到了实现。它们提供了跨平台的压缩功能,可以广泛应用于各种硬件和操作系统。注意,并不是所有操作系统都支持相同的文件属性或文件系统功能。如果您下载用 zip 压缩过的产品文件并在 Windows 系统上解压缩它,然后将结果文件传输到 CD 或 DVD 中以用于 Linux 安装,您可能会遇到安装问题,原因在于,例如,Windows 系统不支持原始的未压缩文件集所含的符号链接。

有关这些或其他压缩程序的更多信息,请参考相关的手册页。





回页首


归档文件

tarcpiodd 命令被常用于备份文件组甚至是整个分区,目的是归档文件或传输到另外一个用户或站点。LPIC-2 认证中的考试 201 关注更加详细的备份事项。

有三种常用的备份方法:

  1. 差异累积 备份是指对自上一次完全备份之后发生更改的所有内容进行备份。要执行恢复,需要最近一次的完全备份和最新的差异备份。
  2. 增量 备份是指只对自上一次增量备份后发生变化的内容执行备份。要执行恢复,需要最近一次的完全备份以及这次完全备份之后的所有增量备份(按顺序)。
  3. 完全 备份是指一次完整的备份,通常为整个文件系统、目录或相关文件组。这种备份所需的时间是三种备份中最长的,因此通常与其他两种方法的其中一种结合使用。

这些命令以及本文中学习的其他命令为您提供了执行任何这些备份任务的工具。

使用 tar

tar(最初来自 Tape ARchive)从一组输入文件或目录中创建一个归档文件,即 tarfiletarball;它还从此类归档中恢复文件。如果某个目录被作为输入提供给 tar,所有文件和子目录都被自动包括,这使得 tar 可以非常方便地归档您的目录结构的子树。

输出可以被导入到文件、磁带或软盘之类的设备或是 stdout 中。输出位置使用 -f 选项指定。其他常见选项为:-c 用于创建归档,-x 用于提取归档,-v 用于详细输出,它将列出被处理的文件,-z 用于使用 gzip 压缩,而 -j 用于使用 bzip2 压缩。大部分 tar 选项包含使用一个连字符的简单格式和使用一对连字符的详细格式。这里解释了简单格式。参考手册页,获得有关详细格式和额外选项的内容。

清单 30 展示了如何使用 tar 创建 lpi103-2 目录的备份。


清单 30. 使用 tar 备份 lpi103-2 目录
				
[ian@echidna lpi103-2]$ tar -cvf ../lpitar1.tar .
./
./text3
./yab
...
./f5

您通常希望压缩归档文件来节省空间或减少传输时间。tar 命令的 GNU 版本允许您使用一个单个选项实现此目的 — -z 表示使用 gzip 压缩,而 -b 表示使用 bzip2 压缩。清单 31 演示了 -z 选项的使用,以及两种归档文件在大小方面的差异。


清单 31. 使用 gzip 压缩 tar 归档
				
[ian@echidna lpi103-2]$ tar -zcvf ../lpitar2.tar ~/lpi103-2/
tar: Removing leading `/' from member names
/home/ian/lpi103-2/
/home/ian/lpi103-2/text3
/home/ian/lpi103-2/yab
...
/home/ian/lpi103-2/f5
[ian@echidna lpi103-2]$ ls -l ../lpitar*
-rw-rw-r--. 1 ian ian 30720 2009-09-24 15:38 ../lpitar1.tar
-rw-rw-r--. 1 ian ian   881 2009-09-24 15:39 ../lpitar2.tar

清单 31 还显示了 tar 的另外一个重要特性。我们使用了一个绝对目录路径,而输出的第一行告诉我们 tar 正在从成员名称中删除前导斜线 (/)。这允许将文件恢复到其他位置以进行检验,如果您尝试恢复系统文件的话,这一点尤其重要。如果您确实需要存储绝对名称,那么使用 -p 选项。在创建归档时,避免将绝对路径名和相对路径名混在一起始终是一种好的想法,因为在从归档恢复时,所有名称都将为相对名称。

tar 命令可以使用 -r--append 选项将额外的文件附加到归档中。这会造成归档中出现文件的多个副本。在这种情况下,最后 一个文件将在恢复操作期间被恢复。您可以使用 --occurrence 选项从多个文件中选择一个特定文件。如果归档位于常规文件系统而非磁带中,那么可以使用 -u--update 选项来更新一个归档。这种工作方式类似于附加到一个归档,区别在于,归档中的文件的时间戳将与文件系统中的时间戳进行比较,并且只有在归档版本之后发生修改的文件被附加。如前所述,这不适用于磁带归档。

tar 命令也可以将归档与当前文件系统加以比较,并恢复来自归档的文件。使用 -d--compare--diff 选项执行比较。输出将显示内容有差异的文件,以及时间戳不同的文件。一般情况下,将只列出不同的文件(如果有的话)。使用前面讨论的 -v 选项获得详细输出。-C-- directory 选项将告诉 tar 从指定的目录开始执行操作,而不是从当前目录开始。

清单 32 展示了一些例子。我们使用 touch 来修改 f1 文件的时间戳,然后演示了 tar 的比较操作,之后从其中一个归档中恢复 f1。我们将使用各种操作来进行说明。


清单 32. 使用 tar 执行比较和恢复
				
[ian@echidna lpi103-2]$ touch f1
[ian@echidna lpi103-2]$ tar --diff --file ../lpitar1.tar .
./f1: Mod time differs
[ian@echidna lpi103-2]$ tar -df ../lpitar2.tar -C /
home/ian/lpi103-2/f1: Mod time differs
[ian@echidna lpi103-2]$ tar -xvf ../lpitar1.tar ./f1 # See below
./f1
[ian@echidna lpi103-2]$ tar --compare -f ../lpitar2.tar --directory /
			

您为恢复指定的文件或目录必须匹配归档中的名称。在本例中,如果尝试只恢复 f1 而不是 ./f1,那么将是无效的。您可以使用 globbing,但是需要注意不要恢复过多或过少的内容。如果您不确定归档内容的话,您可以使用 --list-t 选项来列出归档内容。清单 33 展示了一个通配符说明示例,它将恢复更多的文件,而不仅仅是 ./f1。


清单 33. 使用 tar 列出归档内容
				
[ian@echidna lpi103-2]$ tar -tf ../lpitar1.tar "*f1*"
./f1a
./f1

可以使用 find 命令选择要归档的文件,然后将结果传递给 tar。我们将在讨论 cpio 时探讨这种方法,但是同样的方法也可以用于 tar

和本文介绍的其他命令一样,这篇简要的介绍无法涵盖所有的选项。请参考手册页或信息页了解更多内容。

使用 cpio

cpio 命令在 copy-out 模式下创建归档,在 copy-in 模式下恢复归档,或在 copy-pass 模式下将一组文件从一个位置复制到另一个位置。您将对 copy-out 模式使用 -o--create 选项,对 copy-in 模式使用 -i--extract 选项,而对 copy-pass 模式使用 -p--pass-through 选项。输入是在 stdin 中提供的一组文件。输出被指向 stdout,或者是由 -f--file 选项指定的设备或文件。

清单 34 展示了如何使用 find 命令生成一组文件,并将列表传递给 cpio。注意,对 find 使用了 -print0 选项来为文件名生成 null-terminate 字符串,而 cpio 上的对应的 --null 选项将读取这个格式。这将正确地处理包含内嵌空格或换行符的文件名。-depth 选项告诉 find 在目录名的前面列出目录条目。在本例中,我们仅仅创建了 lpi103-2 目录的两个归档,一个使用相对名称,一个使用绝对名称。我们没有使用 find 的众多功能对所选文件进行限制,比如只查找在本周修改过的文件。


清单 34. 使用 cpio 备份目录
				
[ian@echidna lpi103-2]$ find . -depth -print0 | cpio --null -o > ../lpicpio.1
3 blocks
[ian@echidna lpi103-2]$ find ~/lpi103-2/ -depth -print0 | cpio --null -o > ../lpicpio.2
4 blocks

如果您希望按照归档顺序列出文件,那么对 cpio 添加 -v 选项。

copy-in 模式下的 cpio 命令(选项 -i--extract)可以列出归档的内容或恢复所选文件。当您列出文件时,指定 --absolute-filenames 选项将减少无关消息的数量,较旧版本的 cpio 在从包含前导 / 字符的每个路径中分离该字符时会发出这些消息。该选项在许多最新实现中都被忽略。选择性列出前面的归档的输出如清单 35 所示。


清单 35. 使用 cpio 列出和恢复所选文件
				
[ian@echidna lpi103-2]$ cpio  -i --list  "*backup*" < ../lpicpio.1
backup
backup/text1.bkp.1
backup/text1.bkp.2
3 blocks
[ian@echidna lpi103-2]$ cpio  -i --list absolute-filenames "*text1*" < ../lpicpio.2
/home/ian/lpi103-2/text10
/home/ian/lpi103-2/backup/text1.bkp.1
/home/ian/lpi103-2/backup/text1.bkp.2
/home/ian/lpi103-2/text1
4 blocks

清单 36 展示了如何将路径中含有 “text1” 的所有文件恢复到一个临时子目录中。其中一些文件位于子目录中。与 tar 不同,如果目录树不存在的话,您将需要明确地指定 -d--make-directories 选项。此外,cpio 不会使用归档副本替换文件系统中任何较新的文件,除非您指定了 -u--unconditional 选项。


清单 36. 使用 cpio 恢复所选文件
				
[ian@echidna lpi103-2]$ mkdir temp
[ian@echidna lpi103-2]$ cd temp
[ian@echidna temp]$ cpio  -idv "*f1*" "*.bkp.1" < ../../lpicpio.1
f1a
f1
backup/text1.bkp.1
3 blocks
[ian@echidna temp]$ cpio  -idv "*.bkp.1" < ../../lpicpio.1
cpio: backup/text1.bkp.1 not created: newer or same age version exists
backup/text1.bkp.1
3 blocks
[ian@echidna temp]$ cpio  -id --no-absolute-filenames "*text1*" < ../../lpicpio.2
cpio: Removing leading `/' from member names
4 blocks
./home/ian/lpi103-2/backup/text1.bkp.1
./home/ian/lpi103-2/backup/text1.bkp.2
./home/ian/lpi103-2/text1
./backup/text1.bkp.1
[ian@echidna temp]$ cd ..
[ian@echidna lpi103-2]$ rm -rf temp # You may remove these after you have finished
			

有关其他选项的更多细节,请参考手册页。

dd 命令

就其最简单形式而言,dd 命令将一个输入文件复制到一个输出文件。您已经了解过 cp 命令,因此您可能希望知道为什么还要用另外一个命令复制文件。dd 命令可以完成很多常规 cp 命令无法办到的事情。特别是,它可以对文件执行转换,比如将小写转换为大写,或将 ASCII 转换为 EBCDIC。它还可以重新阻塞(reblock)一个文件,当将文件传输给磁带时可能需要这样做。它还可以跳过或只包括所选的文件块。最后,它可以读取和写入原始设备,比如 /dev/sda,这允许您创建和恢复作为完整分区映像的文件。写入到设备通常需要根权限。

我们将首先来看一个使用 conv 将文件转换为大写的简单例子,如清单 37 所示。我们使用 if 选项指定输入文件,而不是使用默认的 stdin。类似的 of 选项可用于覆盖 stdout 默认输出。为了演示的目的,我们使用 ibsobs 选项指定了不同的输入和输出块大小。对于较大的文件,在进行磁盘之间的传输时,使用较大的块大小加速操作会比较方便。另外,块大小最常用于磁带。注意,清单末尾的三行状态代码表示有多少完整的和局部的块被读取和写入,以及被传输的数据的总量。


清单 37. 使用 dd 将文本转换为大写
				
[ian@echidna lpi103-2]$ cat text6
1 apple
2 pear
3 banana
9       plum
3       banana
10      apple
1 apple
2 pear
3 banana
9       plum
3       banana
10      apple
[ian@echidna lpi103-2]$ dd if=text6 conv=ucase ibs=20 obs=30
1 APPLE
2 PEAR
3 BANANA
9       PLUM
3       BANANA
10      APPLE
1 APPLE
2 PEAR
3 BANANA
9       PLUM
3       BANANA
10      APPLE
4+1 records in
3+1 records out
98 bytes (98 B) copied, 0.00210768 s, 46.5 kB/s

任何文件都可能是一个原始设备。这种情况通常出现在磁带中,但是一个完整的磁盘分区,比如 /dev/hda1 或 /dev/sda2,可以被备份到一个文件或磁带中。理想情况下,设备上的文件系统应当被解除挂载,或至少为只读挂载,从而确保数据在备份期间不会发生修改。清单 39 展示了一个示例,其中输入文件是一个原始设备 dev/sda3,而输出文件是位于根用户的主目录中的文件 backup-1。要将文件转储到磁带或软盘中,将进行 of=/dev/fd0of=/dev/st0 等指定。


清单 38. 使用 dd 备份分区
				
[root@echidna ~]# dd if=/dev/sda2 of=backup-1
1558305+0 records in
1558305+0 records out
797852160 bytes (798 MB) copied, 24.471 s, 32.6 MB/s

注意,797,852,160 字节的数据被复制,而输出文件也包含这么多的数据,即使实际上只会使用 3% 的分区。除非您使用硬件压缩将文件复制到磁盘,否则您将希望对数据进行压缩。清单 39 展示了实现此目的的一种方法,以及 lsdf 命令的输出,向您展示了文件的大小和 /dev/sda3 上的文件系统的使用百分比。


清单 39. 使用 dd 实现压缩备份
				
[root@echidna ~]# dd if=/dev/sda2 |gzip >backup-2
1558305+0 records in
1558305+0 records out
797852160 bytes (798 MB) copied, 23.4617 s, 34.0 MB/s
[root@echidna ~]# ls -l backup-[12]
-rw-r--r--. 1 root root 797852160 2009-09-25 17:13 backup-1
-rw-r--r--. 1 root root    995223 2009-09-25 17:14 backup-2
[root@echidna ~]# df -h /dev/sda2
Filesystem            Size  Used Avail Use% Mounted on
/dev/sda2             755M   18M  700M   3% /grubfile

gzip 压缩将文件大小减小到未压缩大小的 20%。然而,未使用的块可能包含任何数据,因此即使压缩过的备份也可能要比分区的总数据大。

如果按照处理的记录的数量对总字节进行划分,您将看到 dd 将写入多个包含 512 字节的数据块。当复制到磁带之类的原始输出设备中时,这将导致产生非常低效的操作。如前所述,指定 obs 选项来改变输出的大小,或用 ibs 选项来指定输入块大小。您还可以仅指定 bs 来将输入和输出块大小同时指定为一个常用值。在使用磁带时,记住使用相同的块大小来读取和写入磁带。

如果需要多个磁带或其他可移动存储来保存备份,那么需要使用 split 之类的工具将它们分为更小的部分。如果需要跳过磁盘或磁带标签之类的块,可以使用 dd 完成。参见手册页中的示例。

dd 命令无法被文件系统感知,因此需要恢复一个分区的转储才能知道分区的内容。清单 40 展示了如何将清单 39 中转储的文件恢复到分区 /dev/sdc,该分区创建在可移动 USB 驱动上,专门用于这个用途。


清单 40. 使用 dd 恢复分区
				
[root@echidna ~]# gunzip backup-2 -c | dd  of=/dev/sdc7
1558305+0 records in
1558305+0 records out
797852160 bytes (798 MB) copied, 30.624 s, 26.1 MB/s

您可能对一些使用 dd 命令在幕后执行实际的设备写入工作的 CD 和 DVD 刻录应用程序感兴趣。如果您使用的工具提供了大量实际使用的命令,并且您现在对 dd 有了更多的了解,那么您会发现查看日志会大有裨益。实际上,如果您将一个 ISO 映像刻录到一张 CD 或 DVD 磁盘,一种确定没有错误的方法就是使用 dd 回读磁盘并通过 cmp 工具传递结果。清单 41 使用我们在本文中创建的备份文件解释了这一常见技巧,而不是使用 ISO 映像。注意,我们使用映像的文件大小计算读取的块的数量。


清单 41. 比较映像和文件系统
				
[root@echidna ~]# ls -l backup-1
-rw-r--r--. 1 root root 797852160 2009-09-25 17:13 backup-1
[root@echidna ~]# echo $(( 797852160 / 512 )) # calculate number of 512 byte blocks
1558305
[root@echidna ~]# dd if=/dev/sdc7 bs=512 count=1558305 | cmp - backup-1
1558305+0 records in
1558305+0 records out
797852160 bytes (798 MB) copied, 26.7942 s, 29.8 MB/s



参考资料

学习

获得产品和技术

讨论


关于作者

Ian Shields

Ian Shields 参与 developerWorks Linux 专区的许多 Linux 项目。他是 IBM 北卡罗莱那州 Research Triangle Park 的一名高级程序员。他于 1973 年作为一名系统工程师加入 IBM 位于澳大利亚堪培拉的子公司。之后,在加拿大蒙特利尔和北卡罗莱那州 RTP 从事通信系统和普及运算。他拥有多项专利。他毕业于 Australian National University,本科学位是纯粹数学和哲学。他拥有北卡罗来纳州立大学的计算机硕士和博士学位。




对本文的评价










回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款