开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第19天,点击查看活动详情
前言
“我们怎样查找东西?”
我们将察看两个用来在系统中查找文件的工具。
这些工具是:
-
locate –通过名字来查找文件
-
find –在目录层次结构中搜索文件
我们也将看一个经常与文件搜索命令一起使用的命令,它用来处理搜索到的文件列表:
- xargs –从标准输入生成和执行命令行
另外,我们将介绍两个命令来协助我们探索:
-
touch –更改文件时间
-
stat –显示文件或文件系统状态
一、查找文件
1.1 locate – 查找文件的简单方法
这个 locate 程序快速搜索路径名数据库,并且输出每个与给定字符串相匹配的文件名。 比如说,例如,我们想要找到所有名字以“zip”开头的程序。因为我们正在查找程序,可以假定包含匹配程序的目录以 “bin/” 结尾。
locate 简单使用
因此,我们试着以这种方式使用 locate 命令,来找到我们的文件:
[me@linuxbox ~]$ locate bin/zip
locate 命令将会搜索它的路径名数据库,输出任一个包含字符串“bin/zip”的路径名:
/usr/bin/zip
/usr/bin/zipcloak
/usr/bin/zipgrep
/usr/bin/zipinfo
/usr/bin/zipnote
/usr/bin/zipsplit
locate 结合grep
如果搜索要求没有这么简单,locate 可以结合其它工具,比如说 grep 命令,来设计更加有趣的搜索:
[me@linuxbox ~]$ locate zip | grep bin
/bin/bunzip2
/bin/bzip2
/bin/bzip2recover
/bin/gunzip
/bin/gzip
/usr/bin/funzip
/usr/bin/gpg-zip
/usr/bin/preunzip
/usr/bin/prezip
/usr/bin/prezip-bin
/usr/bin/unzip
/usr/bin/unzipsfx
/usr/bin/zip
/usr/bin/zipcloak
/usr/bin/zipgrep
/usr/bin/zipinfo
/usr/bin/zipnote
/usr/bin/zipsplit
这个 locate 程序已经存在了很多年了,它有几个不同的变体被普遍使用着。在现在 Linux发行版中发现的两个最常见的变体是 slocate 和 mlocate,但是通常它们被名为 locate 的符号链接访问。不同版本的 locate 命令拥有重复的选项集合。一些版本包括正则表达式匹配(我们会在下一章中讨论)和通配符支持。查看 locate 命令的手册,从而确定安装了哪个版本的locate 程序。
locate 数据库来自何方? 你可能注意到了,在一些发行版中,仅仅在系统安装之后,locate 不能工作,但是如果你第二天再试一下,它就工作正常了。怎么回事呢?locate 数据库由另一个叫做 updatedb 的程序创建。通常,这个程序作为一个 cron 工作例程周期性运转; 也就是说,一个任务在特定的时间间隔内被 cron 守护进程执行。大多数装有 locate的系统会每隔一天运行一回 updatedb 程序。因为数据库不能被持续地更新,所以当使用 locate 时,你会发现目前最新的文件不会出现。为了克服这个问题,有可能手动运行 updatedb 程序,更改为超级用户身份,在提示符下运行 updatedb 命令。
1.2 find – 查找文件的复杂方式
locate 程序只能依据文件名来查找文件,而 find 程序能基于各种各样的属性,搜索一个给 定目录(以及它的子目录),来查找文件。
find 命令的最简单使用是,搜索一个或多个目录。例如,输出我们的主目录列表。
[me@linuxbox ~]$ find ~
对于最活跃的用户帐号,这将产生一张很大的列表。因为这张列表被发送到标准输出,我们可以把这个列表管道到其它的程序中。让我们使用 wc 程序来计算出文件的数量:
[me@linuxbox ~]$ find ~ | wc -l
47068
Tests
比如说我们想要目录列表。我们可以添加以下测试条件:
[me@linuxbox ~]$ find ~ -type d | wc -l
1695
添加测试条件 -type d 限制了只搜索目录。相反地,我们使用这个测试条件来限定搜索普通文件:
[me@linuxbox ~]$ find ~ -type f | wc -l
38737
这里是 fifind 命令支持的普通文件类型测试条件:
我们也可以通过加入一些额外的测试条件,根据文件大小和文件名来搜索:让我们查找所有文件名匹配通配符模式“*.JPG”和文件大小大于 1M 的文件:
[me@linuxbox ~]$ find ~ -type f -name "\*.JPG" -size +1M | wc -l
840
在这个例子里面,我们加入了 -name 测试条件,后面跟通配符模式。注意,我们把它用双引号引起来,从而阻止 shell 展开路径名。紧接着,我们加入 -size 测试条件,后跟字符串“+1M”。开头的加号表明我们正在寻找文件大小大于指定数的文件。若字符串以减号开头,则意味着查找小于指定数的文件。若没有符号意味着“精确匹配这个数”。结尾字母“M”表明测量单位是兆字节。
下面的字符可以被用来指定测量单位:
fifind 命令支持大量不同的测试条件。下表是列出了一些常见的测试条件。请注意,在需要数值参数的情况下,可以应用以上讨论的“+”和 “-” 符号表示法:
操作符
即使拥有了find 命令提供的所有测试条件,我们还需要一个更好的方式来描述测试条件之间的逻辑关系。
例如,如果我们需要确定是否一个目录中的所有的文件和子目录拥有安全权限,怎么办呢?
我们可以查找权限不是 0600 的文件和权限不是 0700 的目录。幸运地是,find命令提供了一种方法来结合测试条件,通过使用逻辑操作符来创建更复杂的逻辑关系。
为了表达上述的测试条件,我们可以这样做:
[me@linuxbox ~]$ find ~ \( -type f -not -perm 0600 \) -or \( -type d -not -perm 0700 \)
呀!这的确看起来很奇怪。这些是什么东西?实际上,这些操作符没有那么复杂,一旦你知道了它们的原理。这里是操作符列表:
通过这张操作符列表,我们重建 fifind 命令。从最外层看,我们看到测试条件被分为两组,由一个 -or 操作符分开:
( expression 1 ) -or ( expression 2 )
这很有意义,因为我们正在搜索具有不同权限集合的文件和目录。如果我们文件和目录两者都查找,那为什么要用 -or 来代替 -and 呢?因为 fifind 命令扫描文件和目录时,会计算每一个对象,看看它是否匹配指定的测试条件。我们想要知道它是具有错误权限的文件还是有错误权限的目录。它不可能同时符合这两个条件。所以如果展开组合起来的表达式,我们能这样解释它:
( file with bad perms ) -or ( directory with bad perms )
下一个挑战是怎样来检查“错误权限”,这个怎样做呢?我们不从这个角度做。我们将测试“不是正确权限”,因为我们知道什么是“正确权限”。对于文件,我们定义正确权限为 0600,目录则为 0711。测试具有“不正确”权限的文件表达式为:
-type f -and -not -perms 0600
1.3 预定义的操作
find 命令允许基于搜索结果来执行操作。有许多预定义的操作和几种方式来应用用户定义的操作。
首先,让我们看一下几个预定义的操作:
1.4 用户定义的行为
除了预定义的行为之外,我们也可以唤醒随意的命令。传统方式是通过 -exec 行为。这个行为像这样工作:
-exec command {} ;
这里的 command 就是指一个命令的名字,{} 是当前路径名的符号表示,分号是要求的界定符表明命令结束。这里是一个使用 -exec 行为的例子,其作用如之前讨论的 -delete 行为:
-exec rm '{}' ';'
重述一遍,因为花括号和分号对于 shell 有特殊含义,所以它们必须被引起来或被转义。也有可能交互式地执行一个用户定义的行为。通过使用 -ok 行为来代替 -exec,在执行每个指定的命令之前,会提示用户:
find ~ -type f -name 'foo*' -ok ls -l '{}' ';'
< ls ... /home/me/bin/foo > ? y
-rwxr-xr-x 1 me me 224 2007-10-29 18:44 /home/me/bin/foo
< ls ... /home/me/foo.txt > ? y
-rw-r--r-- 1 me me 0 2008-09-19 12:53 /home/me/foo.txt
在这个例子里面,我们搜索以字符串“foo”开头的文件名,并且对每个匹配的文件执行 ls -l 命令。使用 -ok 行为,会在 ls 命令执行之前提示用户
1.5 提高效率
当 -exec 行为被使用的时候,若每次找到一个匹配的文件,它会启动一个新的指定命令的实例。我们可能更愿意把所有的搜索结果结合起来,再运行一个命令的实例。例如,而不是像这样执行命令:
ls -l file1
ls -l file2
我们更喜欢这样执行命令:
ls -l file1 file2
xargs
这个 xargs 命令会执行一个有趣的函数。
它从标准输入接受输入,并把输入转换为一个特定命令的参数列表。
对于我们的例子,我们可以这样使用它:
find ~ -type f -name 'foo\*' -print | xargs ls -l
-rwxr-xr-x 1 me me 224 2007-10-29 18:44 /home/me/bin/foo
-rw-r--r-- 1 me me 0 2008-09-19 12:53 /home/me/foo.txt