LINUX下查找文件的方式很多:如果你想知道某个程序的二进制文件 、源码以及它的manpage是在什么地方,可以用whereis,它会在一些常见目录(具体可man whereis)搜索该文件名,由于这几个路径是硬编码在程序里的,查询速度快,灵活性差;如果你想通过文件名查找一般文件,那么Ubuntuer一般会选择locate,它通过维护一个覆盖全文件系统的文件名数据库来实现快速查找,不过数据库的更新有一定的迟滞性,且它在其他发行版中不一定默认安装。

然而,最强大的*nix通用搜索利器莫过于find。它支持按照文件名、文件内容、文件属性、类型、权限、大小、创建/修改时间等等来查找文件,并且可以灵活地组合各种条件,熟练地运用它会给你带来不少便利。

  1. find基本

    find命令的一般格式是:find [选项] [路径] [条件表达式] [动作表达式]。find会根据选项遍历路径所指明的整棵目录树,对遍历到的所有文件(文件夹也是一种特殊的文件)如果符合条件则执行动作。

    选项包括不显示隐藏文件、限制递归深度(最大深度、最小深度、不访问/仅访问叶子节点)、以及一些调试输出选项等等。

    路径即执行搜索的路径,默认是当前目录,当然你也可以用 . 来明确这一点。

    最博大精深的其实是表达式,它分为测试表达式——用以判断文件是否符合标准,即过滤器;以及动作表达式——对文件执行操作。默认的表达式是-print,这是一个动作表达式,将会输出搜索到的文件。表达式之间是存在逻辑关系的,默认是-and(-a),当然也有-or(-o)、-not(!)等等,其优先级关系与一般逻辑表达式相同,且注意括号中的缩写的优先级比全称高,可以用括号来明确优先级。

  2. 简单常用的操作(仅包含测试表达式)

    你可以试试看在你的主目录执行find,它将会递归地遍历你的整棵主目录树,并且输出所有遍历到的文件的相对路径,包括隐藏文件(以.开头或以~结束的)。我相信你不会等它执行完的,果断ctrl-c掐掉吧……

    下面介绍一些常用的测试表达式:

    • -name:判断文件名
    • -type:判断文件类型,d表示目录、f表示普通文件、l表示符号链接文件、p表示管道文件、b表示块设备文件、c表示字符设备文件
    • -perm:判断文件权限
    • -user/-group:判断文件的属主或属组
    • -nouser/-nogroup:没有有效属主或属组的文件(HINT:如果你想查找不属于某个用户或组的文件,应该使用!-user/!-group)
    • -size:判断文件大小,默认单位是块,如果带上 c 则以字节为单位
      1
      find / -size 0c   #查找系统中的零字节文件
    • -atime/-ctime/-mtime:最近多少内访问文件/改变文件状态/改变文件数据
    • -amin/-cmin/-mmin:与上一条类似,只不过单位是分钟
  3. 结合动作表达式

    有时候我们并不仅仅希望列出所有文件,还希望对搜索到的文件进行一些操作,这时候我们可以结合上动作表达式。

    常见动作表达式:

    • -print0:不换行输出
    • -printf:输出一个格式串,类似C语言中的printf,具体的占位符请查看manpage
    • -fprint/-fprint0/-fprintf:与-print/-print0/-printf相同,只不过是输出到文件中
    • -delete:删除文件

    接下来是重头戏——-exec,可以对find到的文件执行任意命令。-exec后要跟上一个COMMAND串,其中用 {} 替代搜索到的文件,COMMAND必须以 ; 结束(由于shell的关系,实际中必须用\;来转义)。这样说来比较抽象,举个例子吧:

    1
    find . -name foo -exec rm {} \;   #删除当前目录及其子目录下所有文件名为foo的文件

    上述例子是极其危险的,万一你手抖可能造成悲惨下场。所以,若要执行破坏性操作的时候,推荐采用两种较为安全的办法:1.先不加-exec,列出列表看是否有问题;2.使用-ok替代-exec,它会在每次操作之前进行提示。

    这里必须提醒一点,-exec的基础其实是-print,它会把所有的查找结果print到 {} 中,最后再一起执行,即最后实际上只执行了一条命令,而这样就有可能导致命令长度过长,超过shell限制而溢出无法执行。而-ok则是每次确认后就执行一条命令。

    可是实际中有时候需要操作大量文件,-exec会溢出,-ok一条条确认也不现实,这时候就需要xargs了。xargs其实是一条独立的命令,并非find的动作表达式,它的作用是从标准输入中建立并执行命令,具体用法大家可以自己探索,我们这里只讲如何配合find执行一些简单的操作。我们需要通过管道把find的输出传递给xargs,所以上面那个例子还可以这样写:

    1
    find . -name foo | xargs rm

    HINTS:有时候我们需要root权限才能执行某个操作,这时候需要使用sudo。根据管道的知识,我们应该在xargs前加上sudo而不是在find前。

  4. 一个简单的实例

    我最近有管理一台LINUX服务器,需要对WEB目录树的权限做一些改造,出于某些目的,我希望把所有文件都改成664权限。很多人第一念头是直接使用chmod:

    1
    chmod -R 664 /var/www

    然而这样做会把所有的目录也变为664权限,这是灾难性的,目录一旦没有了可执行权限将无法进入。所以实际上我需要把目录改成775权限。这样一来简单的chmod就无法实现了,我们必须配合上find:

    1
    2
    find /var/www -type f | xargs chmod 664
    find /var/www -type d | xargs chmod 775

    搞定!

最后,想要深入学习find命令的,可以查看find –help、man find,以及这两篇文章:

Linux文件查找命令find,xargs详述

Linux Find 命令精通指南