神刀安全网

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

概述

本教程将介绍操作文件和目录的基本 Linux 命令。学习:

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

Linux 文件和目录

Linux 和 UNIX® 系统上的所有文件都可在以 / 为根的单个大型树结构文件系统中访问。您可以通过 挂载卸载 在这棵树中添加更多的分支和删除分支。挂载和卸载已在 文件系统的挂载和卸载 中介绍。

在本教程中,我们将使用教程 “ 学习 Linux 101:文本流和过滤器 ” 中创建的文件来练习这些命令。如果您认真学习了这个教程,那么您应该已经在主目录中创建了一个目录 lpi103-2。如果没有这么做,可以使用系统上的另一个目录来练习本教程中讨论的命令。

关于本系列

本教程系列可以帮助您学习 Linux 系统管理任务。您还可以使用这些教程中的材料对 Linux Professional Institute 的 LPIC-1:Linux 服务器专业认证考试 进行应考准备。

请参阅 “ 学习 Linux,101 :LPIC-1 学习路线图 ”,获得本系列中每篇教程的说明和链接。这个路线图正在开发之中,它反映了 2015 年 4 月 15 日更新的 4.0 版 LPIC-1 考试目标。在完成这些教程后,我们会将它们添加到路线图中。

本教程可以帮助您针对 Linux Server Professional (LPIC-1) 考试 101 的主题 103 中的目标 103.3 进行应考准备。该目标的权重为 3。

前提条件

要从本系列教程中获得最大收获,您应该拥有 Linux 的基本知识和一个正常工作的 Linux 系统,您可以在这个系统上实践本教程中涵盖的命令。有时,程序的不同版本会得到不同的输出格式,所以您的结果可能并不总是与这里给出的清单和图完全相同。

列出目录

文件和目录名称可以是 绝对的 (表示它们以 / 开头),也可以是与 当前工作目录 相对的 (表示它们不是以 / 开头)。文件或目录的绝对路径包含一个 /,后跟 0 或多个目录名称,每个名称后跟另一个 /,然后是最终的名称。

除非另行说明,本教程中的示例使用了 Ubuntu 14.04.2 LTS 和 3.16 内核。您在其他系统上的结果可能有所不同。

列出目录条目

给定一个与当前工作目录相对的文件或目录名称,简单地串联工作目录的绝对名称、/ 和相对名称。例如,我们在之前的教程中创建的目录 lpi103-2 是在我的主目录 /home/ian 中创建的,所以它的完整或绝对路径是 /home/ian/lpi103-2。

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

清单 1. 列出目录条目

ian@Z61t-u14:~/lpi103-2$ echo "$PWD" /home/ian/lpi103-2 ian@Z61t-u14:~/lpi103-2$ ls sedtab  text1  text2  text3  text4  text5  text6  xaa  xab  yaa  yab ian@Z61t-u14:~/lpi103-2$ ls "$PWD" sedtab  text1  text2  text3  text4  text5  text6  xaa  xab  yaa  yab ian@Z61t-u14:~/lpi103-2$ ls /home/ian/lpi103-2 sedtab  text1  text2  text3  text4  text5  text6  xaa  xab  yaa  yab

您可以看到,可以提供相对或绝对目录名称作为 ls 命令的参数,该命令将列出此目录的内容。

列出细节

在存储设备上,文件或目录包含在一个 集合中。有关某个文件的信息包含在一个 inode 中,它记录所有者、最后访问该文件的时间、文件的大小、它是否是目录,以及谁可以读取或写入它等信息。inode 编号也称为 文件序列号 ,该编号在特定文件系统中是唯一的。可以使用 -l (或 --format=long )选项来显示 inode 中存储的一些信息。

默认情况下, ls 命令不会列出特殊文件,也就是名称以点 (.) 开头的文件。根目录以外的每个目录都拥有至少两个特殊条目:该目录本身 (.) 和父目录 (..)。根目录没有父目录。

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

清单 2. 显示一个长目录清单

ian@Z61t-u14:~/lpi103-2$ ls -al total 52 drwxrwxr-x  2 ian ian 4096 Jun  8 17:09 . drwxr-xr-x 15 ian ian 4096 Jun  8 13:26 .. -rw-rw-r--  1 ian ian    8 Jun  8 17:02 sedtab -rw-rw-r--  1 ian ian   24 Jun  8 13:26 text1 -rw-rw-r--  1 ian ian   25 Jun  8 13:36 text2 -rw-rw-r--  1 ian ian   63 Jun  8 16:19 text3 -rw-rw-r--  1 ian ian   26 Jun  8 16:19 text4 -rw-rw-r--  1 ian ian   24 Jun  8 16:42 text5 -rw-rw-r--  1 ian ian   98 Jun  8 17:09 text6 -rw-rw-r--  1 ian ian   15 Jun  8 15:48 xaa -rw-rw-r--  1 ian ian    9 Jun  8 15:48 xab -rw-rw-r--  1 ian ian   17 Jun  8 15:48 yaa -rw-rw-r--  1 ian ian    8 Jun  8 15:48 yab

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

  • 第一个字段(在本例中为 drwxrwxr-x 或 -rw-rw-r–)告诉我们,该文件是目录 (d) 还是常规文件 (-)。您还能够看到一些特殊文件(比如 /dev 文件系统中的文件)的符号链接 (l) 或其他值。可以在教程 创建和更改硬链接和符号链接 中了解符号链接的更多信息(参见系列路线图)。类型的后面是所有者、所有者所在组的成员和每个人的 3 组权限(比如 rwx 或 r–)。这 3 个值分别表示用户、组或每个人拥有读 (r)、写 (w) 或执行 (x) 权限。其他用法(比如 setuid)已在教程 管理文件权限和所有权 中介绍。
  • 下一个字段是一个数字,它会告诉您该文件的 硬链接 数量。我说过 inode 包含文件的信息。该文件的目录条目包含文件的 inode 硬链接(或指针),所以列出的每个条目都至少应该拥有一个硬链接。目录条目中有一个 . 条目和针对每个子目录条目的一个条目。所以我们可以从 清单 2 看到,我的主目录(表示为 ..)包含许多子目录,因为它有 15 个硬链接。
  • 接下来的两个字段是文件的所有者和所有者的主要组。一些系统(比如 Red Hat 或 Fedora 系统)默认情况下为每个用户提供了不同的组。在其他系统上,所有用户都可以位于一个或一些组中。
  • 下一个字段包含文件的长度(以字节为单位)。
  • 倒数第二个字段包含最后修改的时间戳。时间戳格式取决于您的语言环境和日期本身。在我的语言环境中,从今年开始到现在的天数的时间戳显示为 3 个字符的月份缩写、一月中的第几天和 HH:MM 格式的时间。更老的文件或具有未来的日期的文件会使用年份取代 HH:MM 组件。您可以在下面的下看到更多示例。
  • 最后一个字段包含文件或目录的名称。

ls 命令的 -i 选项将显示您的 inode 数量。您会在本教程后面和教程 创建和更改硬链接和符号链接 中再次看到 inode(参见系列路线图)。

多个文件

也可以为 ls 命令指定多个参数,其中每个名称是一个文件或目录的名称。对于目录名称, ls 命令列出了目录的内容,而不是目录本身的信息。在我们的示例中,假设我们想要获得父目录中列出的 lpi103-2 目录条目本身的信息。命令 ls -l ../lpi103-2 会为我们提供一个类似前面的例子的清单。清单 3 展示了如何添加 -d 选项来列出目录条目的信息,而不是目录的内容,以及如何列出多个文件或目录的条目。

清单 3. 使用 ls -d

ian@Z61t-u14:~/lpi103-2$ ls -ld ../lpi103-2 sedtab xaa drwxrwxr-x 2 ian ian 4096 Jun  9 13:01 ../lpi103-2 -rw-rw-r-- 1 ian ian    8 Jun  8 17:02 sedtab -rw-rw-r-- 1 ian ian   15 Jun  8 15:48 xaa

请注意,lpi103-2 的修改时间与前一个清单中的时间不同。另外,与前一个清单中一样,它与该目录中任何文件的时间戳都不同。这是否是您想要的结果?通常应该不是。但是,在开发本教程的过程中,我创建了一些额外示例,然后删除了它们,所以目录时间戳反映了这一事实。您稍后可以在中了解文件时间的更多信息。

排序输出

默认情况下, ls 按字母顺序列出文件。可按许多选项来排序输出。例如, ls -t 按修改时间(从最新到最旧)排序,而 ls -lS 生成一个按大小(从最大到最小)排序的长清单。添加 -r 可反转排序顺序。例如,使用 ls -lrt 生成一个从最旧到最新排序的长清单。请参阅手册页,了解列出文件和目录的其他方式。

复制、移动和删除文件

您现在已经了解了一些创建文件的方式,但我们假设您想复制文件,重命名文件,为文件系统分层结构中移动它们,甚至是删除它们。可以使用 3 个短命令实现这些目的。

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. 您创建了 text 1 的第二个备份副本,这一次将副本放在备份目录中,并显示所有 3 个文件都具有不同的 inode。
  4. 然后将您的 text1.bkp 移动到备份目录,然后重命名该文件,与第二个备份保持一致。尽管使用一个命名命令就可以完成此过程,但这里使用了两个命令来演示此操作。
  5. 再次检查 inode 并确认具有 inode 934193 的 text1.bkp 不再在您的 lpi103-2 目录中,该 inode 是备份目录中的 text1.bkp.1 的 inode。

清单 4. 复制和移动文件

ian@Z61t-u14:~/lpi103-2$ cp text1 text1.bkp ian@Z61t-u14:~/lpi103-2$ mkdir backup ian@Z61t-u14:~/lpi103-2$ cp text1 backup/text1.bkp.2 ian@Z61t-u14:~/lpi103-2$ ls -i text1 text1.bkp backup 787425 text1  787445 text1.bkp  backup: 787447 text1.bkp.2 ian@Z61t-u14:~/lpi103-2$ mv text1.bkp backup ian@Z61t-u14:~/lpi103-2$ mv backup/text1.bkp backup/text1.bkp.1 ian@Z61t-u14:~/lpi103-2$ ls -i text1 text1.bkp backup ls: cannot access text1.bkp: No such file or directory 787425 text1  backup: 787445 text1.bkp.1  787447 text1.bkp.2

通常, cp 命令会复制一个文件来覆盖现有副本,只要现有文件是可写的。另一方面,如果目标存在, mv 就不会移动或重命名文件。有一些与 cpmv 的这一行为相关的有用选项。

-f--force
导致 cp 尝试删除现有目标文件,即使该文件是不可写的。
-i--interactive
要求在尝试替换现有文件之前进行确认。
-b--backup
对任何要替换的文件进行备份。

跟平常一样,请参阅手册页,了解这些选项和其他用于复制和移动的选项的完整细节。

清单 6 演示了包含备份和文件删除的复制过程。

清单 5. 创建备份副本并删除文件

ian@Z61t-u14:~/lpi103-2$ cp text2 backup ian@Z61t-u14:~/lpi103-2$ cp --backup=t text2 backup ian@Z61t-u14:~/lpi103-2$ ls backup text1.bkp.1  text1.bkp.2  text2  text2.~1~ ian@Z61t-u14:~/lpi103-2$ rm backup/text2 backup/text2.~1~ ian@Z61t-u14:~/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@Z61t-u14:~/lpi103-2$ mkdir dir1 dir2

请注意,成功完成时没有输出,但可以使用 echo $? 确认退出代码确实为 0。

如果您想创建嵌套的子目录,比如 d1/d2/d3,可能会失败,因为 d1 和 d2 目录不存在。幸运的是, mkdir 有一个 -p 选项允许创建任何所需的父目录,如 清单 7 所示。

清单 7. 创建父目录

ian@Z61t-u14:~/lpi103-2$ mkdir d1/d2/d3 mkdir: cannot create directory ‘d1/d2/d3’: No such file or directory ian@Z61t-u14:~/lpi103-2$ echo $? 1 ian@Z61t-u14:~/lpi103-2$ mkdir -p d1/d2/d3 ian@Z61t-u14:~/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@Z61t-u14:~/lpi103-2$ cp text1 d1/d2 ian@Z61t-u14:~/lpi103-2$ rmdir -p d1/d2/d3 dir1 dir2 rmdir: failed to remove directory ‘d1/d2’: Directory not empty ian@Z61t-u14:~/lpi103-2$ ls . d1/d2 .: backup  sedtab  text2  text4  text6  xab  yab d1      text1   text3  text5  xaa    yaa  d1/d2: text1 ian@Z61t-u14:~/lpi103-2$ rm d1/d2/text1 ian@Z61t-u14:~/lpi103-2$ rmdir -p d1/d2

处理多个文件和目录

目前为止,您使用的命令仅处理了一个文件或一些单独命名的文件。在本教程的剩余部分,您将了解各种操作,包括处理多个文件,递归操作目录树的一部分,保存或还原多个文件或目录。

递归操作

递归式清单

ls 命令有一个 -R (请注意是大写字母 “R”)选项用于列出目录和它的所有子目录。递归选项仅适用于目录名称;举例而言,它不会在目录树中找到所有名为 “text1” 的文件。可以将您已看到的其他选项与 -R 结合使用。我们的 lpi103-2 目录的递归清单(包括 inode 数量)如清单 9 所示。

清单 9. 递归显示目录清单

ian@Z61t-u14:~/lpi103-2$ ls -iR .: 787446 backup  787425 text1  787431 text3  787433 text5  787427 xaa  787429 yaa 787434 sedtab  787426 text2  787432 text4  787435 text6  787428 xab  787430 yab  ./backup: 787445 text1.bkp.1  787447 text1.bkp.2

递归复制

您可以使用 -r (或 -R--recursive )选项让 cp 命令进入来源目录并递归复制内容。要预防无限递归,您不能复制来源目录本身。清单 10 展示了如何将 lpi103-2 目录中的所有内容都复制到 copy1 子目录中。您使用 ls -R 显示了结果目录树。

清单 10. 递归复制

ian@Z61t-u14:~/lpi103-2$ cp -pR . copy1 cp: cannot copy a directory, ‘.’, into itself, ‘copy1’ ian@Z61t-u14:~/lpi103-2$ ls -R .: backup  sedtab  text2  text4  text6  xab  yab copy1   text1   text3  text5  xaa    yaa  ./backup: text1.bkp.1  text1.bkp.2  ./copy1: backup  text2  text4  text5  text6  xaa  yaa  ./copy1/backup: text1.bkp.1  text1.bkp.2

递归删除

我之前提到过, rmdir 命令仅删除空目录。您可以使用 -r (或 -R--recursive )选项让 rm 命令同时删除文件 目录,如 清单 11 所示,其中您删除了刚创建的 copy1 目录以及它的内容,包括备份子目录和它的内容。

清单 11. 递归删除

ian@Z61t-u14:~/lpi103-2$ rm -r copy1 ian@Z61t-u14:~/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 的程序),使您能够使用通配符模式指定多个文件。

一个包含字符 “?”、“*” 或 “[” 中的任何一个字符的字符串就是 通配符模式 。shell(或者可能另一个程序)可以使用 Globbing 过程将这些模式扩展为一组与该模式匹配的路径名。匹配过程是采用以下方式完成的:

?
匹配任何单个字符。
*
匹配任何字符串,包括空字符串。
[
引入一个 字符类 。字符类是一个非空字符串,以 “]” 终止。匹配表示与括号内的任何单个字符匹配。有一些特殊的考虑因素:
  • “*” 和 “?” 字符匹配它们自己。如果在文件名中使用这些字符,您需要注意正确地引用或转义。
  • 因为该字符串必须是非空的且以 “]” 终止,因此,如果您想要匹配该字符串,必须 将 “]” 放在字符串中。
  • 两个字符之间的 “-” 表示一个范围,其中包含另外两个字符和它们之间的所有字符(按整理顺序排列)。例如,[0-9a-fA-F] 表示任何大写或小写的十六进制数。您可以将 “-” 放在范围中的开头或末尾来与该字符匹配。
  • 指定为范围的第一个字符的 “!” 会对范围进行补足,以便它能与除剩余字符外的任何字符匹配。例如,[!0-9] 表示除数字 0 到 9 的任何字符。除第一个位置外的任何位置的 “!” 会匹配自己。请记住,“!” 也可与 shell history 函数结合使用,所以您需要小心地正确转义它。

备注:通配符模式和正则表达式模式具有一些相同的特征,但它们 并不 相同。请特别小心。

Globbing 可单独应用于路径名的每个组成部分。既不能将它与 “/” 匹配,也不能将它包含在范围中。您可以在可指定多个文件或目录名称的任何地方使用它,例如在 lscpmvrm 命令中。在 清单 12 中,首先创建两个奇怪命名的文件,然后使用具有通配符模式的 lsrm 命令。

清单 12. 通配符模式示例

ian@Z61t-u14:~/lpi103-2$ echo odd1>'text[*?!1]' ian@Z61t-u14:~/lpi103-2$ echo odd2>'text[2*?!]' ian@Z61t-u14:~/lpi103-2$ ls backup  text1       text2       text3  text5  xaa  yaa sedtab  text[*?!1]  text[2*?!]  text4  text6  xab  yab ian@Z61t-u14:~/lpi103-2$ ls text[2-4] text2  text3  text4 ian@Z61t-u14:~/lpi103-2$ ls text[!2-4] text1  text5  text6 ian@Z61t-u14:~/lpi103-2$ ls text*[2-4]* text2  text[2*?!]  text3  text4 ian@Z61t-u14:~/lpi103-2$ ls text*[!2-4]* # Surprise! text1  text[*?!1]  text[2*?!]  text5  text6 ian@Z61t-u14:~/lpi103-2$ ls text*[!2-4] # Another surprise! text1  text[*?!1]  text[2*?!]  text5  text6 ian@Z61t-u14:~/lpi103-2$ echo text*>text10 ian@Z61t-u14:~/lpi103-2$ ls */!* text[*?!1]  text[2*?!] ian@Z61t-u14:~/lpi103-2$ ls *[x/!]* text1       text10  text[2*?!]  text4  text6  xab text[*?!1]  text2   text3       text5  xaa ian@Z61t-u14:~/lpi103-2$ ls *[y/!]* text[*?!1]  text[2*?!]  yaa  yab ian@Z61t-u14:~/lpi103-2$ ls tex?[[]* text[*?!1]  text[2*?!] ian@Z61t-u14:~/lpi103-2$ rm tex?[[]* ian@Z61t-u14:~/lpi103-2$ ls *b* sedtab  xab  yab  backup: text1.bkp.1  text1.bkp.2 ian@Z61t-u14:~/lpi103-2$ ls backup/*2 backup/text1.bkp.2 ian@Z61t-u14:~/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@Z61t-u14:~/lpi103-2$ echo text* text1 text10 text2 text3 text4 text5 text6 ian@Z61t-u14:~/lpi103-2$ echo "text*" text* ian@Z61t-u14:~/lpi103-2$ echo text[[/!?]z?? text[[!?]z??

有关 globbing 的更多信息,请查看 man 7 glob 。您需要小节编号,因为第 3 节中也有 glob 信息。理解各种各样的 shell 交互的最佳方式是实践,所以请抓住一些机会试验这些通配符。记得试用 ls 来检查通配符模式,然后再允许使用 cpmv ,或者在更糟的情况下允许使用 rm 执行一些意外的操作。

接触文件

现在让我们看看 touch 命令,它可以更新文件访问和修改时间,或者创建空文件。在下一部分中,您可以看到如何使用此信息来查找文件和目录。对于这些例子,您可以继续使用 lpi103-2 目录。您还会看到可用来指定时间戳的各种方式。

touch

不带选项的 touch 命令接受一个或多个文件名作为参数,并更新这些文件的 修改 时间。这个时间戳通常与长目录清单中显示的相同。清单 14 使用我们的老朋友 echo 来创建一个小文件 f1,然后使用一个长目录清单显示修改时间(或 mtime )。在本例中,该时间恰好是创建该文件的时间。使用 sleep 命令等待 60 秒,然后再次运行 ls 。请注意,文件的时间戳已改变了 1 分钟。

清单 14. 使用 touch 更新修改时间

ian@Z61t-u14:~/lpi103-2$ echo xxx>f1; ls -l f1; sleep 60; touch f1; ls -l f1 -rw-rw-r-- 1 ian ian 4 Jun  9 17:03 f1 -rw-rw-r-- 1 ian ian 4 Jun  9 17:04 f1

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

清单 15. 使用 touch 创建空文件

ian@Z61t-u14:~/lpi103-2$ touch f2; touch -c f3; ls -l f* -rw-rw-r-- 1 ian ian 4 Jun  9 17:04 f1 -rw-rw-r-- 1 ian ian 0 Jun  9 17:17 f2

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

清单 16. 使用 touch 设置 mtime

[ian@atticf20 lpic-1]$ touch -t 201408121510.59 f3 [ian@atticf20 lpic-1]$ touch -d 11am f4 [ian@atticf20 lpic-1]$ touch -d "last fortnight" f5 [ian@atticf20 lpic-1]$ touch -d "yesterday 6am" f6 [ian@atticf20 lpic-1]$ touch -d "380 days ago 12:00" f7 [ian@atticf20 lpic-1]$ touch -d "tomorrow 02:00" f8 [ian@atticf20 lpic-1]$ touch -d "5 Nov" f9 [ian@atticf20 lpic-1]$ ls -lrt f* -rw-rw-r--. 1 ian ian 0 May 25  2014 f7 -rw-rw-r--. 1 ian ian 0 Aug 12  2014 f3 -rw-rw-r--. 1 ian ian 0 May 26 17:22 f5 -rw-rw-r--. 1 ian ian 0 Jun  8 06:00 f6 -rw-rw-r--. 1 ian ian 0 Jun  9 11:00 f4 -rw-rw-r--. 1 ian ian 0 Jun 10  2015 f8 -rw-rw-r--. 1 ian ian 0 Nov  5  2015 f9

如果您不确定解析某个日期表达式后可能获得的日期,可以使用 date 命令来确定。结合使用 -d 选项和一个日期字符串,解析 touch 能够解析的相同类型的日期格式。请注意清单中针对去年日期或未来日期的不同日期格式。

您可以结合使用 -r (或 --reference )选项和一个 引用文件名 来表明 touch (或 date )应该使用现有文件的时间戳。清单 17 给出了一些例子。

清单 17. 来自引用文件的时间戳

ian@Z61t-u14:~/lpi103-2$ date Tue Jun  9 17:35:02 EDT 2015 ian@Z61t-u14:~/lpi103-2$ date -r f1 Tue Jun  9 17:04:04 EDT 2015 ian@Z61t-u14:~/lpi103-2$ touch -r f1 f1a ian@Z61t-u14:~/lpi103-2$ ls -l f1* -rw-rw-r-- 1 ian ian 4 Jun  9 17:04 f1 -rw-rw-r-- 1 ian ian 0 Jun  9 17:04 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@Z61t-u14:~/lpi103-2$ cat f1 xxx ian@Z61t-u14:~/lpi103-2$ ls -lu f1* -rw-rw-r-- 1 ian ian 4 Jun  9 17:39 f1 -rw-rw-r-- 1 ian ian 0 Jun  9 17:04 f1a ian@Z61t-u14:~/lpi103-2$ ls -l f1* -rw-rw-r-- 1 ian ian 4 Jun  9 17:04 f1 -rw-rw-r-- 1 ian ian 0 Jun  9 17:04 f1a ian@Z61t-u14:~/lpi103-2$ touch -a -r f1a f1 ian@Z61t-u14:~/lpi103-2$ ls -lu f1* -rw-rw-r-- 1 ian ian 4 Jun  9 17:04 f1 -rw-rw-r-- 1 ian ian 0 Jun  9 17:04 f1a

有关许多允许的时间和日期规范的更完整信息,请参阅 touchdate 命令的手册或信息页。

查找文件

现在我们已经介绍了文件和目录主题,通过递归工具处理所有内容,通过 globbing 工具更加选择性地处理内容,接下来让我们看看 find 命令,它更像是一把瑞士军刀。 find 命令用于在一个或多个目录树中查找文件,搜索条件包括名称、时间戳或大小等。我们仍将使用 lpi103-2 目录。

find

find 命令使用所有或部分名称,或者使用其他搜索条件(比如大小、类型、文件所有者、创建日期或最后访问日期)来搜索文件或目录。最基本的 find 格式是按名称或名称的一部分来搜索。清单 19 显示了来自您的 lpi103-2 目录的一个例子,在该示例中,首先搜索了所有在名称中包含 “1” 或 “k” 的文件,然后执行了一些路径搜索(在下面的备注中将会解释)。

清单 19. 按名称查找文件

ian@Z61t-u14:~/lpi103-2$ find . -name "*[1k]*" ./text10 ./f1a ./backup ./backup/text1.bkp.2 ./backup/text1.bkp.1 ./f1 ./text1 ian@Z61t-u14:~/lpi103-2$ find . -iwholename "*ACK*1" ./backup/text1.bkp.1 ian@Z61t-u14:~/lpi103-2$ find . -iwholename "*ACK*/*1" ./backup/text1.bkp.1

备注:

  1. 您可以采用的模式是 shell 通配符模式,比如您之前在下看到的模式。请小心使用通配符。您需要将它们放在引号中,以便 shell 将该字符串传递给 find ,而不是传递与该字符串匹配的文件列表。
  2. 您可以使用 -wholename 代替 -name 来匹配完整路径,而不是只匹配基础文件名。在这种情况下,模式 可能 涵盖路径组件,而不像仅匹配路径的一部分的普通通配符匹配。
  3. 如果您想要执行不区分大小写的搜索,如前面的 iwholename 使用例子中所示,可在字符串或模式上执行搜索的 find 选项前添加一个 “i”。 备注: 有时,您可能会看到 pathipath ,而不是 wholenameiwholename ,但这些选项现在已弃用。
  4. 如果想找到名称以点开头的文件或目录,比如 .bashrc 或当前目录 (.), 必须 指定一个前导的点作为模式的一部分。否则,名称搜索会忽略这些文件或目录。

在上面的第一个例子中,您找到了两个文件和一个目录 (./backup)。可以结合使用 -type 参数单字母类型来限制搜索。使用 “f” 表示常规文件,“d” 表示目录,“l” 表示符号链接。请参阅 find 的手册页,了解其他可能的类型。清单 20 显示了单独搜索目录 ( -type d ) 和按文件名(在本例中使用 * 或任何字符)搜索目录的结果。

清单 20. 按类型查找文件

ian@Z61t-u14:~/lpi103-2$ find . -type d . ./backup ian@Z61t-u14:~/lpi103-2$ find . -type d -name "*" . ./backup

请注意,没有任何名称规范形式的 -type d 规范会显示名称中包含前导的点的目录(在本例中只有当前目录),就像通配符 “*” 一样。

您还可以按文件大小进行搜索,搜索特定大小的文件 (n) 或大于 (+n) 或小于给定值 (-n) 的文件。通过使用大小上限和下限,可以找到大小在给定范围内的文件。默认情况下, find-size 选项会假设单位 “b” 表示 512 字节的数据块。在其他选择中,指定 “c” 表示字节,或者指定 “k” 表示 kb。在 清单 21 中,您首先会找到所有大小为 0 的文件,然后查找所有大小为 24 或 25 字节的文件。请注意,指定 -empty 代替 -size 0 也会找到空文件。

清单 21. 按大小查找文件

ian@Z61t-u14:~/lpi103-2$ find . -size 0 ./f1a ./f2 ian@Z61t-u14:~/lpi103-2$ find . -size -26c -size +23c -print ./text2 ./text5 ./backup/text1.bkp.2 ./backup/text1.bkp.1 ./text1

清单 21 中的第二个示例引入了 -print 选项,它简单地将输出打印到 stdout。这是可对搜索结果采用的 操作 的示例。在 bash shell 中,如果不指定任何操作, -print 是默认操作。在一些系统和一些 shell 中,需要一个操作;否则没有输出。

其他操作包括 -ls (打印文件信息,等效于 ls -lids 命令的输出)和 -exec (对每个文件执行一个命令)。 -exec 需要以分号结尾,必须转义该分号以避免 shell 先解释它。另外,还要在您想在命令中使用所返回文件的地方指定 {}。请记住,花括号对 shell 也有一定的意义,所以必须转义(或放在引号中)。清单 22 展示了如何使用 -ls-exec 选项列出文件信息。请注意,第二种形式没有列出 inode 信息。

清单 22. 查找文件并执行相应操作

ian@Z61t-u14:~/lpi103-2$ find . -size -26c -size +23c -ls 787426    4 -rw-rw-r--   1 ian      ian            25 Jun  8 13:36 ./text2 787433    4 -rw-rw-r--   1 ian      ian            24 Jun  8 16:42 ./text5 787447    4 -rw-rw-r--   1 ian      ian            24 Jun  9 13:09 ./backup/text1.bkp.2 787445    4 -rw-rw-r--   1 ian      ian            24 Jun  9 13:09 ./backup/text1.bkp.1 787425    4 -rw-rw-r--   1 ian      ian            24 Jun  8 13:26 ./text1 ian@Z61t-u14:~/lpi103-2$ find . -size -26c -size +23c -exec ls -l '{}' /; -rw-rw-r-- 1 ian ian 25 Jun  8 13:36 ./text2 -rw-rw-r-- 1 ian ian 24 Jun  8 16:42 ./text5 -rw-rw-r-- 1 ian ian 24 Jun  9 13:09 ./backup/text1.bkp.2 -rw-rw-r-- 1 ian ian 24 Jun  9 13:09 ./backup/text1.bkp.1 -rw-rw-r-- 1 ian ian 24 Jun  8 13:26 ./text1

可以将 -exec 选项用于许多用途,只要您能想得到。例如:

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

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

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

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

对于我们的最后一个 find 示例,您在 touch 命令中使用所描述的时间戳来查找具有特定时间戳且名称以 “t” 开头的文件。首先应将 text2 的时间戳设置为前天,以便可以在输出中看出区别。清单 23 显示了 3 个例子:

  1. -mtime -2 结合使用时, find 命令将会查找在过去两天内修改的所有文件。在这种情况下,一天是与当前日期和时间相对的 24 小时。请注意,如果想要基于访问时间而不是修改时间来查找文件,可以使用 -atime
  2. 添加 -daystart 选项表明您想将每天都视为日历天,从午夜开始计时。现在 text2 文件已从列表中排除。
  3. 最后,展示一下如何使用以分钟数而不是天数表示的时间范围,查找在过去一小时(60 分钟)到 12 小时(720 分钟)之间修改的文件。

清单 23. 按时间戳查找文件

ian@Z61t-u14:~/lpi103-2$ date Tue Jun  9 23:25:31 EDT 2015 ian@Z61t-u14:~/lpi103-2$ touch -d "2 days ago 23:45" text2 ian@Z61t-u14:~/lpi103-2$ find . -mtime -2 -type f -name "t*" -exec ls -l '{}' /; -rw-rw-r-- 1 ian ian 25 Jun  7 23:45 ./text2 -rw-rw-r-- 1 ian ian 26 Jun  8 16:19 ./text4 -rw-rw-r-- 1 ian ian 58 Jun  9 16:46 ./text10 -rw-rw-r-- 1 ian ian 98 Jun  8 17:09 ./text6 -rw-rw-r-- 1 ian ian 24 Jun  8 16:42 ./text5 -rw-rw-r-- 1 ian ian 24 Jun  9 13:09 ./backup/text1.bkp.2 -rw-rw-r-- 1 ian ian 24 Jun  9 13:09 ./backup/text1.bkp.1 -rw-rw-r-- 1 ian ian 63 Jun  8 16:19 ./text3 -rw-rw-r-- 1 ian ian 24 Jun  8 13:26 ./text1 ian@Z61t-u14:~/lpi103-2$ find . -daystart -mtime -2 -type f -name "t*" -exec ls -l '{}' /; -rw-rw-r-- 1 ian ian 26 Jun  8 16:19 ./text4 -rw-rw-r-- 1 ian ian 58 Jun  9 16:46 ./text10 -rw-rw-r-- 1 ian ian 98 Jun  8 17:09 ./text6 -rw-rw-r-- 1 ian ian 24 Jun  8 16:42 ./text5 -rw-rw-r-- 1 ian ian 24 Jun  9 13:09 ./backup/text1.bkp.2 -rw-rw-r-- 1 ian ian 24 Jun  9 13:09 ./backup/text1.bkp.1 -rw-rw-r-- 1 ian ian 63 Jun  8 16:19 ./text3 -rw-rw-r-- 1 ian ian 24 Jun  8 13:26 ./text1 ian@Z61t-u14:~/lpi103-2$ find . -mmin -720 -mmin +60 -type f -name "t*" -exec ls -l '{}' /; -rw-rw-r-- 1 ian ian 58 Jun  9 16:46 ./text10 -rw-rw-r-- 1 ian ian 24 Jun  9 13:09 ./backup/text1.bkp.2 -rw-rw-r-- 1 ian ian 24 Jun  9 13:09 ./backup/text1.bkp.1

find 命令的手册页可帮助您了解各种各样的选项,本文篇幅有限,无法一一介绍它们。

识别文件

文件名通常包含 gif、jpeg 或 html 等后缀,提供了该文件可能包含的内容的提示。Linux 不需要这些后缀,一般情况下不会使用它们来识别文件类型。了解自己在处理何种类型的文件,可帮助您了解使用何种程序来显示或操作它。 file 命令提供一个或多个文件中的数据类型的信息。清单 24 显示了使用 file 命令的一些示例。

清单 24. 识别文件内容

ian@Z61t-u14:~/lpi103-2$ ile backup text1 f2 ~/p-ishields.jpg /bin/echo backup:                   directory  text1:                    ASCII text f2:                       empty  /home/ian/p-ishields.jpg: JPEG image data, JFIF standard 1.02 /bin/echo:                ELF 32-bit LSB  executable, Intel 80386, version 1 (SY SV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]= 6aea7ab0e5ad49166cc6c53e121a79174a59ae50, stripped

file 命令尝试使用 3 种测试对每个文件进行分类。举例而言,文件系统测试将会使用 stat 命令的结果来确定某个文件是空的还是一个目录。所谓的 魔力 测试将会检查某个文件是否包含标识它的特定内容。这些签名也称为 幻数 。最后,语言测试将会查看文本文件的内容,以尝试确定文件是 XML 文件、C 或 C++ 语言源代码、troff 文件,还是其他某个某种语言处理器视为源代码的文件。除非指定了 -k--keep-going 选项,否则只会报告找到的第一种类型。

file 命令有许多选项,您可以通过手册页了解它们。清单 25 展示了如何使用 -i (或 --mime )选项将文件类型显示为 MIME 字符串,而不是正常的人类可读输出。

清单 25. 将文件内容识别为 MIME

ian@Z61t-u14:~/lpi103-2$ file -i backup text1 f2 ~/p-ishields.jpg /bin/echo backup:                   inode/directory; charset=binary text1:                    text/plain; charset=us-ascii f2:                       inode/x-empty; charset=binary /home/ian/p-ishields.jpg: image/jpeg; charset=binary /bin/echo:                application/x-executable; charset=binary

幻数文件也通过 file 命令进行管理。同样地,请参阅手册页了解有关的更多信息。

备注: identify 命令(包含在 ImageMagick 包中)是一个附加工具,它可以在识别图像文件类型时提供更多细节。清单 26 显示了一个示例。

清单 26. 使用 ImageMagick 的 identify 命令

ian@Z61t-u14:~/lpi103-2$ identify ~/p-ishields.jpg /home/ian/p-ishields.jpg JPEG 64x80 64x80+0+0 8-bit DirectClass 3.58KB 0.000u 0:00.019

压缩文件

在备份、存档或传输文件时,通常会压缩文件。在 Linux 环境中,3 种流行的压缩程序是 gzipbzip2xzgzip 命令使用 Lempel-Ziv 算法, bzip2 使用 Burrows-Wheeler 块排序算法, xz 使用 Lempel–Ziv–Markov 链算法 (LZMA)。所有 3 种都是无损压缩工具。

使用 gzip 和 gunzip

压缩一般对文本文件很有用。许多图像格式已压缩了数据,所以压缩可能对这些或其他二进制文件没多大效果。为了演示对大型文本文件的压缩,让我们将 /etc/services 复制到您已使用的目录并使用 gzip 压缩它,如 清单 27 所示。您使用 cp-p 选项保留 /etc/services 的时间戳。请注意,压缩的文件具有相同的时间戳,而且有一个 .gz 后缀。

清单 27. 使用 gzip 压缩

ian@Z61t-u14:~/lpi103-2$ cp -p /etc/services . ian@Z61t-u14:~/lpi103-2$ ls -l serv* -rw-r--r-- 1 ian ian 19558 Dec 30  2013 services ian@Z61t-u14:~/lpi103-2$ gzip services ian@Z61t-u14:~/lpi103-2$ ls -l serv* -rw-r--r-- 1 ian ian 7538 Dec 30  2013 services.gz

您使用 gzip-d 选项,或者更常见的情况下,使用 gunzip 命令来解压使用 gzipp 压缩的文件。清单 28 显示了这些选项中的第一个。请注意,未压缩的文件现在拥有原始文件名和时间戳。

清单 28. 使用 gzip 解压

ian@Z61t-u14:~/lpi103-2$ gzip -d services.gz ian@Z61t-u14:~/lpi103-2$ ls -l serv* -rw-r--r-- 1 ian ian 19558 Dec 30  2013 services

使用 bzip2 和 bunzip2

bzip2 命令的操作方式与 gzip 类似,如 清单 29 所示。

清单 29. 使用 bzip2 压缩

ian@Z61t-u14:~/lpi103-2$ ls -l serv* -rw-r--r-- 1 ian ian 19558 Dec 30  2013 services ian@Z61t-u14:~/lpi103-2$ bzip2 services ian@Z61t-u14:~/lpi103-2$ ls -l serv* -rw-r--r-- 1 ian ian 7208 Dec 30  2013 services.bz2 ian@Z61t-u14:~/lpi103-2$ bunzip2 services.bz2  ian@Z61t-u14:~/lpi103-2$ ls -l serv* -rw-r--r-- 1 ian ian 19558 Dec 30  2013 services

使用 xz 和 unxz

xz 命令是一种较新的压缩命令,它使用了一种 Lempel–Ziv–Markov 链算法 (LZMA)(最开始用在 7-Zip 存档程序中)。LZMA2 是一种容器格式,可保存使用不同的 LZMA 参数压缩的数据和完全未压缩的数据。 xz 命令的原生格式 (.xz) 是单个压缩数据流的容器,就这点而言,它类似于 gzipbzip2 。请参阅参考资料 了解有关的更多信息。

毫不奇怪, xz 命令的运行方式与 gzipbzip2 相同,如 清单 30 所示。

清单 30. 使用 xz 压缩

ian@Z61t-u14:~/lpi103-2$ ls -l serv* -rw-r--r-- 1 ian ian 19558 Dec 30  2013 services ian@Z61t-u14:~/lpi103-2$ xz services  ian@Z61t-u14:~/lpi103-2$ ls -l serv* -rw-r--r-- 1 ian ian 7144 Dec 30  2013 services.xz ian@Z61t-u14:~/lpi103-2$ unxz services.xz  ian@Z61t-u14:~/lpi103-2$ ls -l serv* -rw-r--r-- 1 ian ian 19558 Dec 30  2013 services

压缩程序之间的区别

速度 — 更高的压缩率通常需要更多时间时间来执行压缩。

根据设计,许多 bzip2xz 选项与 gzip 的选项相同,但这些命令并不都具有相同的选项。您可能已经注意到,在我们的两个示例中,未压缩的文件具有与原始文件相同的名称和时间戳。但是,重命名或使用 touch 处理压缩文件可能会改变此行为。 gzip 命令提供了 -N--name 选项来强制保留名称和时间戳,但 bzip2xz 没有这些选项。 gzipxz 命令也有一个 -l 选项,用于显示压缩文件的信息,包括将在解压它时使用的名称。清单 31 演示了这些命令之间的一些区别。对于所有 3 个命令, -v--verbose 都提供了压缩信息。

清单 31. gzip、bzip2 和 xz 之间的一些区别

ian@Z61t-u14:~/lpi103-2$ ls -l serv* -rw-r--r-- 1 ian ian 19558 Dec 30  2013 services ian@Z61t-u14:~/lpi103-2$ gzip -Nv services services:  61.6% -- replaced with services.gz ian@Z61t-u14:~/lpi103-2$ touch services.gz ian@Z61t-u14:~/lpi103-2$ mv services.gz services-x.gz ian@Z61t-u14:~/lpi103-2$ ls -l serv* -rw-r--r-- 1 ian ian 7538 Jun 12 13:11 services-x.gz ian@Z61t-u14:~/lpi103-2$ gzip -l services-x.gz          compressed        uncompressed  ratio uncompressed_name                7538               19558  61.6% services-x ian@Z61t-u14:~/lpi103-2$ gzip -lN services-x.gz          compressed        uncompressed  ratio uncompressed_name                7538               19558  61.6% services ian@Z61t-u14:~/lpi103-2$ gunzip -N services-x.gz ian@Z61t-u14:~/lpi103-2$ ls -l serv* -rw-r--r-- 1 ian ian 19558 Dec 30  2013 services ian@Z61t-u14:~/lpi103-2$ bzip2 -v services   services:  2.713:1,  2.948 bits/byte, 63.15% saved, 19558 in, 7208 out. ian@Z61t-u14:~/lpi103-2$ mv services.bz2 services-x.bz2 ian@Z61t-u14:~/lpi103-2$ touch services-x.bz2 ian@Z61t-u14:~/lpi103-2$ ls -l serv* -rw-r--r-- 1 ian ian 7208 Jun 12 13:12 services-x.bz2 ian@Z61t-u14:~/lpi103-2$ bunzip2 services-x.bz2 ian@Z61t-u14:~/lpi103-2$ ls -l serv* -rw-r--r-- 1 ian ian 19558 Jun 12 13:12 services-x ian@Z61t-u14:~/lpi103-2$ ls -l serv* # New date and name -rw-r--r-- 1 ian ian 19558 Jun 12 13:12 services-x ian@Z61t-u14:~/lpi103-2$ rm services-x; cp -p /etc/services . # get fresh copy ian@Z61t-u14:~/lpi103-2$ ls -l serv*  -rw-r--r-- 1 ian ian 19558 Dec 30  2013 services ian@Z61t-u14:~/lpi103-2$ xz -v services services (1/1)   100 %          7,144 B / 19.1 KiB = 0.365                                     ian@Z61t-u14:~/lpi103-2$ mv services.xz services-x.xz ian@Z61t-u14:~/lpi103-2$ touch services-x.xz ian@Z61t-u14:~/lpi103-2$ ls -l serv* -rw-r--r-- 1 ian ian 7144 Jun 12 13:15 services-x.xz ian@Z61t-u14:~/lpi103-2$ unxz services-x.xz ian@Z61t-u14:~/lpi103-2$ ls -l serv*  # New date and name -rw-r--r-- 1 ian ian 19558 Jun 12 13:15 services-x ian@Z61t-u14:~/lpi103-2$ rm services-x # Don't need this any more

所有 3 个压缩程序都使用重定向(管道中的 - 参数)来接受来自 stdin 的输入。都支持使用 -c--stdout 选项将输出定向到 stdout。如果输入来自 stdin,则默认情况下会将输出定向到 stdout。清单 32 给出了一些例子。

清单 32. 结合使用压缩程序与 stdin 和 stdout

ian@Z61t-u14:~/lpi103-2$ cat /etc/services | gzip - -c >servg.gz ian@Z61t-u14:~/lpi103-2$ gzip -l servg.gz           compressed        uncompressed  ratio uncompressed_name                7529               19558  61.6% servg ian@Z61t-u14:~/lpi103-2$ xz - </etc/services > servx.xz ian@Z61t-u14:~/lpi103-2$ xz -l servx.xz Strms  Blocks   Compressed Uncompressed  Ratio  Check   Filename     1       1      7,144 B     19.1 KiB  0.365  CRC64   servx.xz ian@Z61t-u14:~/lpi103-2$ unxz servx.xz ian@Z61t-u14:~/lpi103-2$ ls *serv* servg.gz  servx ian@Z61t-u14:~/lpi103-2$ ls -l *serv* -rw-rw-r-- 1 ian ian  7529 Jun 12 13:39 servg.gz -rw-rw-r-- 1 ian ian 19558 Jun 12 13:40 servx ian@Z61t-u14:~/lpi103-2$ rm serv* # Don't need these now

还有其他两个与 bzip2 有关联的命令:

  1. bzcat 命令将文件解压到 stdout,等效于 bzip2 -dc
  2. bzip2recover 命令尝试从损坏的 bzip2 文件恢复数据。

还有其他 4 个与 xz 有关联的命令:

  1. xzcat 命令等效于 xz --decompress --stdout
  2. lzma 命令等效于 xz --format=lzma
  3. unlzma 命令等效于 xz --format=lzma --decompress
  4. lzcat 命令等效于 xz --format=lzma --decompress --stdout

手册页可以帮助您了解 gzipbzip2xz 的其他选项的更多信息。

其他压缩工具

在 Linux 和 UNIX 系统上,通常仍能找到两个较老的程序: compressuncompress

此外,还为 Linux 实现了来自 Info-ZIP 项目的 zipunzip 命令。这些命令提供了跨平台压缩功能,可用在许多硬件和操作系统上。请注意,不是所有操作系统都支持相同的文件属性或文件系统功能。如果您下载了一个压缩的产品文件,并将它解压到 Windows 系统上,然后将结果文件转移到 CD 或 DVD,以便安装在 Linux 上,那么您可能会遇到安装问题,因为 Windows 系统不支持原始的未压缩文件集中的符号链接。

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

存档文件

tarcpiodd 命令常用于备份文件组,甚至是完整分区,以便能够用于存档,或者传输给另一个用户或站点。考试 201(包含在 LPIC-2:Linux 网络专业认证中)更详细地介绍了备份考虑因素。

有 3 种一般的备份方法:

  • 差异累积 备份是对自上次完整备份以来更改的所有内容的备份。该恢复操作需要上次的完整备份和最新的差异备份。
  • 增量 备份是仅对自上次增量备份以来更改的内容的备份。该恢复操作需要上次完整备份和自上次完整备份以来的所有增量备份(按顺序)。
  • 完整 备份是一种全面的备份,通常会对整个文件系统、目录或相关文件组执行此备份。创建该备份需要花费最长的时间,所以它通常与其他两种方法之一结合使用。

这些命令和您在本教程中学到的其他命令,为您提供了执行所有这些备份任务的工具。

使用 tar

tar (最初来自 Tape ARchive )从一组输入文件或目录创建一个存档文件,或者称为 tarfiletarball ;它还会从存档还原文件。如果提供了一个目录作为 tar 的输入,则会自动包含所有文件和子目录,这使得 tar 对存档您的目录结构的子树非常方便。

输出可以是文件,磁带或磁盘等设备,或者 stdout。输出位置可使用 -f 选项指定。其他常见的选项包括: -c 创建存档, -x 提取存档, -v 提供详细的输出(列出被处理的文件), -z 使用了 gzip 压缩方法, -j 使用了 bzip2 压缩方法。大部分 tar 选项都有一个使用单一连字符的短格式和使用一对连字符的长格式。这里演示了短格式。请参阅手册页,以便了解长格式和其他选项。

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

清单 33. 使用 tar 备份我们的 lpi103-2 目录

[ian@echidna lpi103-2]$ tar -cvf ../lpitar1.tar . ian@Z61t-u14:~/lpi103-2$ tar -cvf ../lpitar1.tar . ./ ./text2 ./text4 ./text10 ... ./text1 ./xab

通常,您希望压缩存档文件来节省空间或减少传输时间。借助 tar 命令的 GNU 版本,只需一个选项即可实现此目的:

  • -z (或 –gzip、–gunzip、–ungzip)表示使用 gzip 压缩
  • -b (或 –bzip2)表示使用 bzip2 压缩
  • -J (或 –xz)表示使用 xz 压缩

清单 34 演示了 -z 选项的用法和两种存档之间的大小区别。

清单 34. 使用 gzip 压缩 tar 存档

ian@Z61t-u14:~/lpi103-2$ tar -zcvf ../lpitar2.tar ~/lpi103-2/ tar: Removing leading `/' from member names /home/ian/lpi103-2/ /home/ian/lpi103-2/text2 /home/ian/lpi103-2/text4 /home/ian/lpi103-2/text10 ... /home/ian/lpi103-2/text1 /home/ian/lpi103-2/xab ian@Z61t-u14:~/lpi103-2$ ls -l ../lpitar* -rw-rw-r-- 1 ian ian 20480 Jun 12 16:39 ../lpitar1.tar -rw-rw-r-- 1 ian ian   703 Jun 12 16:51 ../lpitar2.tar

备注:使用 gzip 压缩的 tar 文件通常以 .tgz 结尾,而不是简单的 .tar,后者更多情况下用于未压缩的文件。这是一种惯例 — tar 命令不依赖于文件扩展名。

清单 34 还展示了 tar 的另一个重要功能。它使用了绝对目录路径,而且输出的第一行会告诉您, tar 从成员名称中删除了前导斜杠 (/)。这使文件能够还原到其他某个位置来进行验证,可能在尝试还原系统文件时特别重要。如果您确实想存储绝对名称,可以使用 -p 选项。在创建存档时避免混合使用绝对路径名称和相对路径名称,这也是一个不错的主意,因为在从存档中还原时,所有文件的路径都将是相对的。

tar 命令可使用 -r--append 选项将其他文件附加到存档中。这可能导致存档中存在文件的多个副本。在这种情况下,在执行还原期间,将会还原 最后 一个副本。可以使用 --occurrence 选项在多个文件中选择某个特定的文件。如果存档位于常规的文件系统而不是磁带上,可以使用 -u--update 选项更新存档。此行为类似于附加到存档,但会对存档中的文件的时间戳与文件系统上的时间戳进行比较,而且只会附加自保存存档版本以后修改的文件。前面已经提到过,这不适用于磁带存档。

tar 命令也可以将存档与当前文件系统进行比较,并从存档中还原文件。可以使用 -d--compare--diff 选项执行比较。输出显示了具有不同内容的文件,以及具有不同时间戳的文件。通常,只会列出不同的文件(如果有)。可以使用之前讨论的 -v 选项获得详细输出。 -C--directory 选项会告诉 tar 从指定的目录而不是当前目录开始执行一个操作。

清单 35 给出了一些例子。它使用 touch 修改 f1 文件的时间戳,然后演示了在从一个存档还原 f1 之前 tar 执行的比较操作。清单 35 使用各种各样的选项格式进行了演示。

清单 35. 使用 tar 进行比较和还原

ian@Z61t-u14:~/lpi103-2$ touch f1 ian@Z61t-u14:~/lpi103-2$ tar --diff --file ../lpitar1.tar . ./f1: Mod time differs ian@Z61t-u14:~/lpi103-2$ tar -df ../lpitar2.tar -C / home/ian/lpi103-2/f1: Mod time differs ian@Z61t-u14:~/lpi103-2$ tar -xvf ../lpitar1.tar ./f1 # See below ./f1 ian@Z61t-u14:~/lpi103-2$ tar --compare -f ../lpitar2.tar --directory /

您指定的要还原的文件或目录必须与存档中的名称相匹配。在本例中,尝试仅还原 f1 而不还原 ./f1 是做不到的。可以使用 globbing,但需要小心地避免还原比您想要的更多或更少的内容。如果不确定存档中有哪些内容,可以使用 --list-t 选项列出存档内容。如果想使用通配符文件名,可使用 --wildcards 选项。清单 36展示了一个通配符规范的示例,它还原的文件不仅是 ./f1。

清单 36. 使用 tar 列出存档内容

ian@Z61t-u14:~/lpi103-2$ tar -tf  ../lpitar1.tar --wildcards "*f1*" ./f1a ./f1

您可以使用 find 命令选择想要存档的文件,然后将结果传输到 tar。我将在讨论 cpio 时讨论此技术,但同样的方法也适用于 tar

与您在这里学到的其他命令一样,有许多选项都未在这篇篇幅有限的文章中介绍。请参阅手册或信息页,了解有关的更多细节。

使用 cpio

copy-out 模式下运行 cpio 命令来创建存档,在 copy-in 模式下还原存档,或者在 copy-pass 模式下将一组文件从一个位置复制到另一个位置。可以使用 -o--create 选项表示 copy-out 模式,使用 -i--extract 选项表示 copy-in 模式,使用 -p--pass-through 选项表示 copy-pass 模式。输入是一个在 stdin 上提供的文件列表。可以输出到 stdout,也可以输出到使用 -f--file 选项指定的设备或文件。

清单 37 展示了如何使用 find 命令生成文件列表,然后将该列表传输到 cpio 。您可以注意到,在 find 上使用了 -print0 选项来生成以 null 终止的文件名字符串,并在 cpio 上使用相应的 --null 选项来读取此格式。这样可以正确处理嵌入了空格或换行符的文件名。 -depth 选项告诉 find 在目录名称之前列出目录条目。在本例中,简单地创建了我们的 lpi103-2 目录的两个存档,一个具有相对名称,另一个具有绝对名称。您没有使用 find 的许多功能来限制所选的文件,比如仅查找本周修改的文件。

清单 37. 使用 cpio 备份目录

ian@Z61t-u14:~/lpi103-2$ find . -depth -print0 | cpio --null -o > ../lpicpio.1 3 blocks ian@Z61t-u14:~/lpi103-2$ find ~/lpi103-2/ -depth -print0 | cpio --null -o > ../lpicpio.2 3 blocks

如果希望看到存档的文件列表,可将 -v 选项添加到 cpio

cpio 选项的 copy-in 模式(选项 -i--extract )可列出存档的内容或还原选定的文件。列出文件时,指定 --absolute-filenames 选项可以减少额外消息的数量,一些较老的 cpio 版本会在从每个拥有前导 / 字符的路径中剥离该字符时发出这些消息。在目前的许多实现中,会静默地忽略此选项。有选择地列出以前的存档的输出,如 清单 38 所示。

清单 38. 使用 cpio 列出和还原选定的文件

ian@Z61t-u14:~/lpi103-2$ cpio  -i --list  "*backup*" < ../lpicpio.1 backup/text1.bkp.2 backup/text1.bkp.1 backup 3 blocks ian@Z61t-u14:~/lpi103-2$ cpio  -i --list absolute-filenames "*text1*" < ../lpicpio.2 /home/ian/lpi103-2/text10 /home/ian/lpi103-2/backup/text1.bkp.2 /home/ian/lpi103-2/backup/text1.bkp.1 /home/ian/lpi103-2/text1 3 blocks

清单 39 展示了如何将路径中包含 “text1” 的所有文件还原到一个临时子目录。其中一些文件位于子目录中。不同于 tar ,如果您的目录树不存在,您需要显式指定 -d--make-directories 选项。此外, cpio 不会将文件系统上的任何较新的文件替换为存档副本,除非指定了 -u--unconditional 选项。

清单 39. 使用 cpio 还原选定的文件

ian@Z61t-u14:~/lpi103-2$ mkdir temp ian@Z61t-u14:~/lpi103-2$ cd temp ian@Z61t-u14:~/lpi103-2/temp$ cpio  -idv "*f1*" "*.bkp.1" < ../../lpicpio.1 f1a backup/text1.bkp.1 f1 3 blocks ian@Z61t-u14:~/lpi103-2/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@Z61t-u14:~/lpi103-2/temp$ cpio  -id -v --no-absolute-filenames "*text1*" < ../../lpicpio.2 cpio: Removing leading `/' from member names home/ian/lpi103-2/text10 home/ian/lpi103-2/backup/text1.bkp.2 home/ian/lpi103-2/backup/text1.bkp.1 home/ian/lpi103-2/text1 3 blocks ian@Z61t-u14:~/lpi103-2/temp$ find . . ./home ./home/ian ./home/ian/lpi103-2 ./home/ian/lpi103-2/text10 ./home/ian/lpi103-2/backup ./home/ian/lpi103-2/backup/text1.bkp.2 ./home/ian/lpi103-2/backup/text1.bkp.1 ./home/ian/lpi103-2/text1 ./f1a ./backup ./backup/text1.bkp.1 ./f1 ian@Z61t-u14:~/lpi103-2/temp$ cd .. ian@Z61t-u14:~/lpi103-2$ rm -rf temp # You may remove these after you have finished

有关其他选项的详细信息,请参阅手册页。

dd 命令

在最简单的形式中, dd 命令将一个输入文件复制到一个输出文件。您已经了解了 cp 命令,所以您可能还想知道为什么要使用另一个命令来复制文件。 dd 命令可以执行常规 cp 无法执行的一些操作。具体地讲,它可以在文件上执行转换,比如将小写转换为大写,或者将 ASCII 转换为 EBCDIC。它还可以重新对文件进行分块,在将文件传输到磁带时可能需要这么做。它可以跳过或仅包含某个文件的选定数据块。最后,它可以读取原始设备上的数据并将数据写入原始设备,比如 /dev/sda,使您能够创建或还原一个完整的分区映像文件。将数据写入设备通常需要根权限。

我们首先查看一个使用 conv 选项将文件转换为大写的简单例子,如 清单 40 所示。使用 if 选项指定输入文件,而不使用默认的 stdin。可以使用类似的 of 选项将默认输出重写到 stdout。出于演示的目的,我们使用 ibsobs 选项指定了不同的输入和输出数据块大小。对于大型文件,在磁盘间传输时,使用更大的数据块来加速操作可能很方便。在其他情况下,数据块大小最常用于磁带。您可以注意到,清单末尾有 3 个状态行,它们显示了读取和写入了多少个完整和部分数据块,以及传输的数据总量。

清单 40. 使用 dd 将文本转换为大写

ian@Z61t-u14:~/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@Z61t-u14:~/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.000421841 s, 232 kB/s

每个文件都可能是原始设备。磁带通常属于这种情况,但可以将整个磁盘分区(比如 /dev/hda1 或 /dev/sda2)备份到一个文件或磁带。理想情况下,应卸载设备上的文件系统或者至少应将它们挂载为只读,以确保数据不会在备份期间发生更改。清单 42 给出了一个例子,其中输入文件是一个原始设备 /dev/sda3,输出文件是根用户主目录中的文件 backup-1。要将该文件转储到磁带或软盘,可以指定类似 of=/dev/fd0of=/dev/st0 的选项。

清单 41. 使用 dd 备份分区

ian@Z61t-u14:~/lpi103-2$ sudo -s [sudo] password for ian:  root@Z61t-u14:~/lpi103-2# cd /root root@Z61t-u14:/root# dd if=/dev/sda2 of=backup-1 9450000+0 records in 9450000+0 records out 4838400000 bytes (4.8 GB) copied, 144.189 s, 33.6 MB/s

请注意,我们复制了 4.8GB 数据,而且输出文件确实那么大,尽管实际上我们只使用了这个特定分区的 3.9GB。除非通过硬件压缩形式将数据复制到磁带,否则您可能希望压缩数据。清单 42 展示了一种通过 xz 传输 dd 输出来完成此任务的方式。再次使用 gzip 作为压缩程序来执行压缩。然后,使用 ls 的输出来查看结果,以便向您展示三个备份文件的大小,最终,您将挂载分区,并使用 df 命令显示 /dev/sda2 上的文件系统的使用百分比。请注意,如果未指定文件, gzipxz 会假设输入来自 stdin,所以您不需要显式指定 - 选项。

清单 42. 使用 dd 和 gzip 备份和压缩

ian@Z61t-u14:~/lpi103-2$ sudo -s [sudo] password for ian:  root@Z61t-u14:~/lpi103-2# cd /root root@Z61t-u14:/root# dd if=/dev/sda2 of=backup-1 9450000+0 records in 9450000+0 records out 4838400000 bytes (4.8 GB) copied, 144.189 s, 33.6 MB/s root@Z61t-u14:/root# dd if=/dev/sda2 |xz >backup-2 9450000+0 records in 9450000+0 records out 4838400000 bytes (4.8 GB) copied, 4637.01 s, 1.0 MB/s root@Z61t-u14:/root# dd if=/dev/sda2 |gzip >backup-3 9450000+0 records in 9450000+0 records out 4838400000 bytes (4.8 GB) copied, 474.107 s, 10.2 MB/s root@Z61t-u14:/root# ls -l backup-* -rw-r--r-- 1 root root 4838400000 Jun 12 18:02 backup-1 -rw-r--r-- 1 root root 3721409720 Jun 12 19:49 backup-2 -rw-r--r-- 1 root root 3790762349 Jun 12 21:56 backup-3 root@Z61t-u14:/root# mkdir /mnt/sda2 root@Z61t-u14:/root# mount /dev/sda2 /mnt/sda2 root@Z61t-u14:/root# df -h /dev/sda2 Filesystem      Size  Used Avail Use% Mounted on /dev/sda2       4.5G  3.9G  613M  87% /mnt/sda2

gzip 压缩可将文件大小减小到未压缩大小的约 78%, xz 可达到约 77%,但压缩所花的时间要长得多。但是,未用的数据块可能包含任意数据,所以如果仅考虑分区上的数据,那么压缩后的备份甚至有可能比预期的大。

如果将复制的总字节数按处理的记录数量来划分,您会看到 dd 将写入 512 字节的数据块。复制到磁带等原始输出设备时,这可能是一个非常低效的操作。前面已经提到,可以指定 obs 选项来更改输出大小,或者使用 ibs 选项来指定输入块大小。还可以仅指定 bs 来将输入和输出块大小设置为相同值。使用磁带时,请记得对读取磁带和写入磁带使用相同的块大小。

如果需要多个磁带或其他可移动存储来存储备份,则需要使用 split 等实用工具将它分解为更小的部分。如果需要跳过一些数据块,比如磁盘或磁带标签,可以使用 dd 实现此操作。请参阅手册页,查看相关的示例。

dd 命令无法感知文件系统,所以您需要还原分区的转储文件来确定它包含哪些内容。清单 43 展示了如何还原转储到 清单 42 中的分区。您可以将它还原到一个空分区(例如 /dev/sda7),以便检查它。

清单 43. 使用 dd 还原分区

root@Z61t-u14:/root# gunzip backup-3 -c | dd  of=/dev/sda7 9450000+0 records in 9450000+0 records out 4838400000 bytes (4.8 GB) copied, 445.272 s, 10.9 MB/s

您可能想知道一些 CD 或 DVD 克隆应用程序在幕后使用 dd 命令来执行实际的设备写入。如果您使用的实用工具提供了实际使用的命令的日志,您可能会发现,查看日志可帮助您进一步了解 dd 。实际上,如果将 ISO 映像克隆到 CD 或 DVD 光盘,那么验证是否存在错误的一种方式是使用 dd 读取光盘,并通过 cmp 实用工具传输结果。清单 44 演示了使用您在本教程中创建的备份文件(而不是 ISO 映像)的一般技术。请注意,它使用了映像的文件大小来计算要读取的数据块数。

清单 44. 比较映像与文件系统。

root@Z61t-u14:/root# ls -l backup-1 -rw-r--r-- 1 root root 4838400000 Jun 12 18:02 backup-1 root@Z61t-u14:/root#  echo $(( 4838400000 / 512 )) # calculate number of 512 byte blocks 9450000 root@Z61t-u14:/root# dd if=/dev/sda7 bs=512 count=9450000 | cmp - backup-1 9450000+0 records in 9450000+0 records out 4838400000 bytes (4.8 GB) copied, 264.092 s, 18.3 MB/s

从这些示例中无法明显看到的一件事是,我将一个 FAT32 分区还原到了一个更大的 EXT4 分区上。您需要使用本系列中另一篇教程 – 学习 Linux,101:创建分区和文件系统 中讨论的一些分区管理工具来更正此情况。

对文件和目录管理的介绍到此就结束了。

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » 学习 Linux,101: 文件和目录管理

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址