本教程将介绍操作文件和目录的基本 Linux 命令。学习:
find
命令根据类型、大小或时间来查找和处理文件 gzip
、 bzip2
和 xz
压缩和解压文件 tar
、 cpio
和 dd
存档文件 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 种不同方式。
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
选项显示所有文件的常用的格式化清单,包括 . 和 .. 目录条目。
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)。剩余行显示目录条目的信息。
ls
命令的 -i
选项将显示您的 inode 数量。您会在本教程后面和教程 创建和更改硬链接和符号链接 中再次看到 inode(参见系列路线图)。
也可以为 ls
命令指定多个参数,其中每个名称是一个文件或目录的名称。对于目录名称, ls
命令列出了目录的内容,而不是目录本身的信息。在我们的示例中,假设我们想要获得父目录中列出的 lpi103-2 目录条目本身的信息。命令 ls -l ../lpi103-2
会为我们提供一个类似前面的例子的清单。清单 3 展示了如何添加 -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
mv
cp
执行复制的相同规则;您可以重命名一个文件或将一组文件移动到一个新目录中。因为名称只是一个链接到 inode 的目录条目,对 inode 数量没有改变不应感到奇怪, 除非 该文件移动到另一个文件系统中,在这种情况下,移动它的行为更像在复制之后删除原始文件。 rm
rename
命令在何处? 如果您习惯使用 DOS 或 Windows 系统,您可能会对使用 mv
重命名文件感到很陌生。Linux 确实有一个 rename
命令,但它具有与同名的 DOS 和 Windows 命令不同的语法。请参阅手册页,了解关于如何使用它的细节。
清单 4 演示了如何使用 cp
和 mv
为我们的文本文件创建一些备份副本。您还使用 ls -i
显示了一些文件的 inode。
mkdir
命令创建一个备份子目录。 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
就不会移动或重命名文件。有一些与 cp
和 mv
的这一行为相关的有用选项。
-f
或 --force
cp
尝试删除现有目标文件,即使该文件是不可写的。 -i
或 --interactive
-b
或 --backup
跟平常一样,请参阅手册页,了解这些选项和其他用于复制和移动的选项的完整细节。
清单 6 演示了包含备份和文件删除的复制过程。
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
,这是删除目录的类似命令。
假设您位于我们的 lpi103-2 目录中,而且您想创建子目录 dir1 和 dir2。像您已检查的命令一样, mkdir
一次可以处理多个目录创建请求,如 清单 6 所示。
ian@Z61t-u14:~/lpi103-2$ mkdir dir1 dir2
请注意,成功完成时没有输出,但可以使用 echo $?
确认退出代码确实为 0。
如果您想创建嵌套的子目录,比如 d1/d2/d3,可能会失败,因为 d1 和 d2 目录不存在。幸运的是, mkdir
有一个 -p
选项允许创建任何所需的父目录,如 清单 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
命令删除目录是创建它们的反过程。同样地,还有一个 -p
选项用于删除父目录。只在目录为空的时候,才能使用 rmdir
删除它,因为没有强制删除选项。在介绍时,您会看到完成这个特定任务的另一种方法。学会此方法后,您可能很少在命令行上使用 rmdir
,但了解它仍然是值得的。
为了演示目录删除,您将 text1 文件复制到目录 d1/d2 中,以便它不再是空的。然后使用 rmdir
删除您刚使用 mkdir
创建的所有目录。可以看到,d1 和 d2 没有被删除,因为 d2 不是空的。其他目录被删除了。从 d2 删除 text1 的副本后,只需调用 rmdir -p
一次即可删除 d1 和 d2。
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 所示。
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
显示了结果目录树。
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 目录以及它的内容,包括备份子目录和它的内容。
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
选项来强制删除它。根用户在执行清理时常常这么做,但要注意,如果您不够小心,可能会丢失宝贵的数据。
通常,您可能需要在许多文件系统对象上执行单一操作,而不像递归操作一样处理整个树。例如,您可能像找到您在 lpi103-2 中创建的所有文本文件的修改时间,而不列出拆分文件。尽管这在我们的小目录中很容易,但在大型文件系统中则困难得多。
要解决此问题,可以使用内置于 bash shell 中的通配符支持。此支持功能也称为 “globbing”(因为它最初实现为一个名为 /etc/glob 的程序),使您能够使用通配符模式指定多个文件。
一个包含字符 “?”、“*” 或 “[” 中的任何一个字符的字符串就是 通配符模式 。shell(或者可能另一个程序)可以使用 Globbing 过程将这些模式扩展为一组与该模式匹配的路径名。匹配过程是采用以下方式完成的:
备注:通配符模式和正则表达式模式具有一些相同的特征,但它们 并不 相同。请特别小心。
Globbing 可单独应用于路径名的每个组成部分。既不能将它与 “/” 匹配,也不能将它包含在范围中。您可以在可指定多个文件或目录名称的任何地方使用它,例如在 ls
、 cp
、 mv
或 rm
命令中。在 清单 12 中,首先创建两个奇怪命名的文件,然后使用具有通配符模式的 ls
和 rm
命令。
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 .* . ..
备注:
ls
示例一样,如果模式扩展得到一个作为目录名称的名称,而且未指定 -d
选项,那么将会列出该目录的内容(与上面针对模式“*b*”的示例一样)。 ls
命令列出了两个特殊的目录条目(. 和 ..)。 请记住,命令中的任何通配符字符都很容易通过 shell 扩展,这可能导致意外的结果。此外,如果您指定一种与任何文件系统对象不匹配的模式,POSIX 会要求将原始模式字符串传递给命令。早先的一些实现将一个 null 列表传递给命令,所以您可能遇到一些具有异常行为的旧脚本。清单 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
来检查通配符模式,然后再允许使用 cp
、 mv
,或者在更糟的情况下允许使用 rm
执行一些意外的操作。
现在让我们看看 touch
命令,它可以更新文件访问和修改时间,或者创建空文件。在下一部分中,您可以看到如何使用此信息来查找文件和目录。对于这些例子,您可以继续使用 lpi103-2 目录。您还会看到可用来指定时间戳的各种方式。
不带选项的 touch
命令接受一个或多个文件名作为参数,并更新这些文件的 修改 时间。这个时间戳通常与长目录清单中显示的相同。清单 14 使用我们的老朋友 echo
来创建一个小文件 f1,然后使用一个长目录清单显示修改时间(或 mtime )。在本例中,该时间恰好是创建该文件的时间。使用 sleep
命令等待 60 秒,然后再次运行 ls
。请注意,文件的时间戳已改变了 1 分钟。
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。
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 给出了一些例子。
[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 给出了一些例子。
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 系统会同时记录文件 修改 时间和文件 访问 时间。这些时间也分别称为 mtime 和 atime 。在创建文件时,两个时间戳会被设置为同一个值,而且都会在修改文件时重设。如果访问了一个文件,那么会更新访问时间,即使没有修改该文件。在最后一个包含 touch
的示例中,我们看看文件 访问 时间。 -a
(或 --time=atime
、 --time=access
或 --time=use
)选项指定应更新访问时间。清单 18 使用 cat
命令访问 f1 文件并显示它的内容。您然后使用 ls -l
和 ls -lu
分别显示 f1 和 f1a 的修改和访问时间,f1a 是使用 f1 作为引用文件来创建的。然后,使用 touch -a
将 f1 的访问时间重设为 f1a 的访问时间,并确认它已重设。
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
有关许多允许的时间和日期规范的更完整信息,请参阅 touch
和 date
命令的手册或信息页。
现在我们已经介绍了文件和目录主题,通过递归工具处理所有内容,通过 globbing 工具更加选择性地处理内容,接下来让我们看看 find
命令,它更像是一把瑞士军刀。 find
命令用于在一个或多个目录树中查找文件,搜索条件包括名称、时间戳或大小等。我们仍将使用 lpi103-2 目录。
find
命令使用所有或部分名称,或者使用其他搜索条件(比如大小、类型、文件所有者、创建日期或最后访问日期)来搜索文件或目录。最基本的 find 格式是按名称或名称的一部分来搜索。清单 19 显示了来自您的 lpi103-2 目录的一个例子,在该示例中,首先搜索了所有在名称中包含 “1” 或 “k” 的文件,然后执行了一些路径搜索(在下面的备注中将会解释)。
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
find
,而不是传递与该字符串匹配的文件列表。 -wholename
代替 -name
来匹配完整路径,而不是只匹配基础文件名。在这种情况下,模式 可能 涵盖路径组件,而不像仅匹配路径的一部分的普通通配符匹配。 iwholename
使用例子中所示,可在字符串或模式上执行搜索的 find
选项前添加一个 “i”。 备注: 有时,您可能会看到 path
和 ipath
,而不是 wholename
或 iwholename
,但这些选项现在已弃用。 在上面的第一个例子中,您找到了两个文件和一个目录 (./backup)。可以结合使用 -type
参数单字母类型来限制搜索。使用 “f” 表示常规文件,“d” 表示目录,“l” 表示符号链接。请参阅 find
的手册页,了解其他可能的类型。清单 20 显示了单独搜索目录 ( -type d
) 和按文件名(在本例中使用 * 或任何字符)搜索目录的结果。
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
也会找到空文件。
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 信息。
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 个例子:
-mtime -2
结合使用时, find
命令将会查找在过去两天内修改的所有文件。在这种情况下,一天是与当前日期和时间相对的 24 小时。请注意,如果想要基于访问时间而不是修改时间来查找文件,可以使用 -atime
。 -daystart
选项表明您想将每天都视为日历天,从午夜开始计时。现在 text2 文件已从列表中排除。 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
命令的一些示例。
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 字符串,而不是正常的人类可读输出。
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 显示了一个示例。
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 种流行的压缩程序是 gzip
、 bzip2
和 xz
。 gzip
命令使用 Lempel-Ziv 算法, bzip2
使用 Burrows-Wheeler 块排序算法, xz
使用 Lempel–Ziv–Markov 链算法 (LZMA)。所有 3 种都是无损压缩工具。
压缩一般对文本文件很有用。许多图像格式已压缩了数据,所以压缩可能对这些或其他二进制文件没多大效果。为了演示对大型文本文件的压缩,让我们将 /etc/services 复制到您已使用的目录并使用 gzip 压缩它,如 清单 27 所示。您使用 cp
的 -p
选项保留 /etc/services 的时间戳。请注意,压缩的文件具有相同的时间戳,而且有一个 .gz 后缀。
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 显示了这些选项中的第一个。请注意,未压缩的文件现在拥有原始文件名和时间戳。
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
命令的操作方式与 gzip
类似,如 清单 29 所示。
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
命令是一种较新的压缩命令,它使用了一种 Lempel–Ziv–Markov 链算法 (LZMA)(最开始用在 7-Zip 存档程序中)。LZMA2 是一种容器格式,可保存使用不同的 LZMA 参数压缩的数据和完全未压缩的数据。 xz
命令的原生格式 (.xz) 是单个压缩数据流的容器,就这点而言,它类似于 gzip
和 bzip2
。请参阅参考资料 了解有关的更多信息。
毫不奇怪, xz
命令的运行方式与 gzip
和 bzip2
相同,如 清单 30 所示。
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
速度 — 更高的压缩率通常需要更多时间时间来执行压缩。
根据设计,许多 bzip2
和 xz
选项与 gzip
的选项相同,但这些命令并不都具有相同的选项。您可能已经注意到,在我们的两个示例中,未压缩的文件具有与原始文件相同的名称和时间戳。但是,重命名或使用 touch 处理压缩文件可能会改变此行为。 gzip
命令提供了 -N
或 --name
选项来强制保留名称和时间戳,但 bzip2
和 xz
没有这些选项。 gzip
和 xz
命令也有一个 -l
选项,用于显示压缩文件的信息,包括将在解压它时使用的名称。清单 31 演示了这些命令之间的一些区别。对于所有 3 个命令, -v
或 --verbose
都提供了压缩信息。
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 给出了一些例子。
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
有关联的命令:
bzcat
命令将文件解压到 stdout,等效于 bzip2 -dc
。 bzip2recover
命令尝试从损坏的 bzip2 文件恢复数据。 还有其他 4 个与 xz
有关联的命令:
xzcat
命令等效于 xz --decompress --stdout
。 lzma
命令等效于 xz --format=lzma
。 unlzma
命令等效于 xz --format=lzma --decompress
。 lzcat
命令等效于 xz --format=lzma --decompress --stdout
。 手册页可以帮助您了解 gzip
、 bzip2
和 xz
的其他选项的更多信息。
在 Linux 和 UNIX 系统上,通常仍能找到两个较老的程序: compress
和 uncompress
。
此外,还为 Linux 实现了来自 Info-ZIP 项目的 zip
和 unzip
命令。这些命令提供了跨平台压缩功能,可用在许多硬件和操作系统上。请注意,不是所有操作系统都支持相同的文件属性或文件系统功能。如果您下载了一个压缩的产品文件,并将它解压到 Windows 系统上,然后将结果文件转移到 CD 或 DVD,以便安装在 Linux 上,那么您可能会遇到安装问题,因为 Windows 系统不支持原始的未压缩文件集中的符号链接。
有关这些或其他压缩程序的更多信息,请参阅各自的手册页。
tar
、 cpio
和 dd
命令常用于备份文件组,甚至是完整分区,以便能够用于存档,或者传输给另一个用户或站点。考试 201(包含在 LPIC-2:Linux 网络专业认证中)更详细地介绍了备份考虑因素。
有 3 种一般的备份方法:
这些命令和您在本教程中学到的其他命令,为您提供了执行所有这些备份任务的工具。
tar
(最初来自 Tape ARchive )从一组输入文件或目录创建一个存档文件,或者称为 tarfile 或 tarball ;它还会从存档还原文件。如果提供了一个目录作为 tar
的输入,则会自动包含所有文件和子目录,这使得 tar
对存档您的目录结构的子树非常方便。
输出可以是文件,磁带或磁盘等设备,或者 stdout。输出位置可使用 -f
选项指定。其他常见的选项包括: -c
创建存档, -x
提取存档, -v
提供详细的输出(列出被处理的文件), -z
使用了 gzip 压缩方法, -j
使用了 bzip2 压缩方法。大部分 tar
选项都有一个使用单一连字符的短格式和使用一对连字符的长格式。这里演示了短格式。请参阅手册页,以便了解长格式和其他选项。
清单 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
选项的用法和两种存档之间的大小区别。
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 使用各种各样的选项格式进行了演示。
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。
ian@Z61t-u14:~/lpi103-2$ tar -tf ../lpitar1.tar --wildcards "*f1*" ./f1a ./f1
您可以使用 find
命令选择想要存档的文件,然后将结果传输到 tar。我将在讨论 cpio
时讨论此技术,但同样的方法也适用于 tar
。
与您在这里学到的其他命令一样,有许多选项都未在这篇篇幅有限的文章中介绍。请参阅手册或信息页,了解有关的更多细节。
在 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
的许多功能来限制所选的文件,比如仅查找本周修改的文件。
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 所示。
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
选项。
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
命令将一个输入文件复制到一个输出文件。您已经了解了 cp
命令,所以您可能还想知道为什么要使用另一个命令来复制文件。 dd
命令可以执行常规 cp
无法执行的一些操作。具体地讲,它可以在文件上执行转换,比如将小写转换为大写,或者将 ASCII 转换为 EBCDIC。它还可以重新对文件进行分块,在将文件传输到磁带时可能需要这么做。它可以跳过或仅包含某个文件的选定数据块。最后,它可以读取原始设备上的数据并将数据写入原始设备,比如 /dev/sda,使您能够创建或还原一个完整的分区映像文件。将数据写入设备通常需要根权限。
我们首先查看一个使用 conv
选项将文件转换为大写的简单例子,如 清单 40 所示。使用 if
选项指定输入文件,而不使用默认的 stdin。可以使用类似的 of
选项将默认输出重写到 stdout。出于演示的目的,我们使用 ibs
和 obs
选项指定了不同的输入和输出数据块大小。对于大型文件,在磁盘间传输时,使用更大的数据块来加速操作可能很方便。在其他情况下,数据块大小最常用于磁带。您可以注意到,清单末尾有 3 个状态行,它们显示了读取和写入了多少个完整和部分数据块,以及传输的数据总量。
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/fd0
或 of=/dev/st0
的选项。
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 上的文件系统的使用百分比。请注意,如果未指定文件, gzip
和 xz
会假设输入来自 stdin,所以您不需要显式指定 -
选项。
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),以便检查它。
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 映像)的一般技术。请注意,它使用了映像的文件大小来计算要读取的数据块数。
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:创建分区和文件系统 中讨论的一些分区管理工具来更正此情况。
对文件和目录管理的介绍到此就结束了。