find命令的使用格式为:find options path expressions
find命令其实有两种options,一种是“真正属于自己的”,另一种位于expressions内部。第一种选项一共就5个:-P -L -H -D -O,其中-D用来显示调试信息;-O用来指定优化级别,后面紧跟一个数字(没有空格),它们俩的具体信息可以看man。而-P -L -H的作用是“control the treatment of symbolic links”,不指定它们中的任意一个时,默认为-P,即不跟踪链接。所谓跟踪链接是指:若被搜索的目录下有一个符号链接,且指向另一个目录,那么被指向的目录也将被搜索。
第二种选项和第一种被path隔开,实际上,第一种选项比较少使用,find后面往往直接就是path,即要搜索的目录。如果不指定path,默认为当前目录。搜索的时候,指定目录的子目录也是被搜索的对象,所以path可以视为start_dir。可以同时指定多个目录。


真正的重头戏是expressions,它也有三部分:options test actions。

由于path后面经常直接就是test,其他两项出现的也比较少,所以先从test开始。

  • -name pattern:匹配文件名为“pattern”的所有文件。大多数情况下,我们都是按照文件名来查找文件的,所以这是使用最频繁的选项。
    要注意:pattern中不应该包含路径名,只有最终的文件名,即basename是pattern,否则会报错,路径名应该在path中指定;这里要求文件名和pattern必须完全一致。如下:

    m@meng:~$ ls tmp/
    onlyme  onlyme1  test.sh
    m@meng:~$ find tmp/ -name onlyme
    tmp/onlyme
    m@meng:~$ find tmp/ -name only
    m@meng:~$

    可以看到,只写一部分名称是不会被匹配的。
    -name选项支持简单的shell字符扩展,如*、[]、?,其中*匹配任意数量的任意字符;?匹配一个任意字符;[]匹配任意一个出现在括号中的字符:

    m@meng:~/tmp$ find . -name onlym[eg]
    ./onlyme
    m@meng:~/tmp$ find . -name ??lyme
    ./onlyme
    m@meng:~/tmp$ find . -name on*
    ./onlyme

    更复杂的扩展就不支持了,这时就需要下面的-regex选项。
    -name还有几个变种:
    -iname:忽略大小写的-name;
    -lname:File is a symbolic link whose contents match shell pattern.这意思是匹配符号链接指向的文件名,而不是符号链接名本身。
    -ilname:同上,忽略大小写。

  • -regex pattern:这里的pattern就可以是完全的正则表达式了,为防意外最好用引号括起来。pattern是用正则表达式表示的file name。
    -regex还有一个变种-iregex,可以忽略大小写。

  • -path pattern:由于-name选项不能使用路径分隔符(name中出现“/”会报错),也就是无法选择子目录,但是某些情况下我们需要指定子目录或排除某些子目录(配合-prune),所以就有了这个选项。
    要注意的是,-path后面的pattern必须以前面指定的目录为起始路径:若指定目录是相对路径“.”,则pattern也必须以“.”开头;若指定目录是绝对路径,则pattern也要把这部分路径加在前面。如下:

    m@meng:~/patches$ ls tmp/
    new  nw  onlyme  test.sh
    m@meng:~/patches$ find . -path ./tmp/on*
    ./tmp/onlyme
    m@meng:~/patches$ find . -path tmp/on*
    m@meng:~/patches$ 
    m@meng:~/patches$ find /home/m/patches/ -path /home/m/patches/tmp/on*
    m@meng:~/patches$ /home/m/patches/tmp/onlyme

    -path也支持简单的元字符扩展,但是为了保险起见,最好把pattern用双引号括起来,如下:

    m@meng:~/patches$ find . -path ./tmp/*
    find: 路径必须在表达式之前: ./tmp/nw
    用法: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...] [expression]
    m@meng:~/patches$ find . -path "./tmp/*"
    ./tmp/nw
    ./tmp/onlyme
    ./tmp/test.sh
    ./tmp/new

    我看到很多文章使用下面的方法来寻找子目录中的文件,本人实验后发现不管用:

    m@meng:~/patches$ find . -path ./tmp
    ./tmp
    m@meng:~/patches$ find . -path ./tmp -name onlyme
    m@meng:~/patches$ 

    我们看到,如果只有子目录的名称(名称后面不要又斜线),那么就只匹配子目录本身,而不会去把子目录下面的所有文件都匹配;而用path指定子目录后,再使用-name指定名称将不会得到想要的结果,所以只能在pattern中把文件名补上。然而下面这种方法可以:

    m@meng:~/patches$ find . -path ./tmp -o -name onlyme
    ./tmp
    ./tmp/onlyme

    这里使用了-o选项,将在后面讨论。


  • -uid n以及-user name:都是根据文件的owner来匹配,前者根据owner的UID,后者根据name。

  • -gid n以及-group name:同上,只是换成了group。
  • -nouser以及nogroup:若某个文件的属主或属组已经被删除,则匹配。


  • -type c:匹配文件类型为c的所有文件。c的具体值包括:f(普通文件)、b(块设备)、c(字符文件)、d(目录)、l(符号链接)等等。

  • -size:根据文件的大小来匹配。支持的单位包括c(bytes)、w(word,2bytes)、b(blocks,512bytes)、k(1024bytes)、M(1024k bytes)、G(1024M bytes)。

    m@meng:~/workspaces$ find . -size 1c 
    ./.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/history.version      ./.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.version
    ./.metadata/.plugins/org.eclipse.cdt.make.core/specs.c
    ./.metadata/.plugins/org.eclipse.cdt.make.core/specs.cpp
    m@meng:~/workspaces$ find . -size -1c 
    ./.metadata/.plugins/com.genuitec.org.hibernate.eclipse/hibernate-tools.log
    ./.metadata/.plugins/org.skyway.core/compass/index/core/clearcache
    m@meng:~/workspaces$ find . -size +2c -size -5c 
    ./.metadata/.plugins/org.eclipse.jdt.core/assumedExternalFilesCache
    ./.metadata/.plugins/org.eclipse.jdt.core/invalidArchivesCache

    test中经常要用到数字,所以必然会涉及数值范围问题。find给出的方法是,在数字前面加“+”表示大于该数字,而“-”表示小于该数字,没有符号表示等于该数字。

  • -inum n与-samefile name:根据是否是同一个文件来匹配。inum表示inode number,当一个文件的indoe与指定的n相同时,匹配;同理,-samefile直接使用文件名,若某个文件与指定的name具有相同的inode,匹配。


  • -readable、-writable、-executable:很显然,这是按文件权限来过滤文件的。比如,加上-executable,那么没有可执行权限的文件将会被过滤掉。
    不过,大家都知道,文件的权限分为三组,即用户(u)、群组(g)和其他(o),每组的权限又分为读(r)、写(w)和执行(x)。那么这三个选项到底属于哪个组呢?我暂时还没研究清楚,不过幸好find还有对权限进行匹配的更详细更常用的选项,就是下面的-perm。

  • -perm mode:根据文件的权限来匹配。但是指定权限mode的时候有三种模式:mode、-mode、/mode。
    纯mode格式意味着严格匹配,即每组权限都必须指定且完全一样,不能只指定其中一组而其他组缺省。
    -mode格式就要宽松许多,比如-o=x,那么只要一个文件的o组至少有x权限就可以匹配(有没有r和w权限无所谓),而其他组不管;再比如-221,那意味着一个文件的u组和g组至少有w权限o组至少有x权限就可以匹配,于是-o=x相当于-001。所以-mode格式是“至少模式”。
    /mode格式还要宽松,在/mode格式指定的三组权限中,只要有一组达到了“至少模式”,就可以成功匹配。可以用一个公式表达/mode,比如/124 <==> -100 || -020 || -004; /u=w,o=r <==> -u=w || -o=r。举几个例子吧:

    m@meng:~/tmp$ ls -lA
    总用量 8
    ---x-w---x 1 m    m     6  623 01:22 onlyme
    -rw-rw-r-- 1 root root  0  627 12:09 onlyme1
    -r-xr-x--x 1 m    m    66  623 02:57 test.sh
    m@meng:~/tmp$ find . -perm /242
    .
    ./onlyme1
    ./test.sh
    m@meng:~/tmp$ find . -perm /go=w
    .
    ./onlyme
    ./onlyme1
    m@meng:~/tmp$ find . -perm /go=x
    .
    ./onlyme
    ./test.sh
    m@meng:~/tmp$ find . -perm -g=x
    .
    ./test.sh
    m@meng:~/tmp$ find . -perm -120
    .
    ./onlyme
    m@meng:~/tmp$ find . -perm 121
    ./onlyme

  • -amin、-atime、-anewer:这三项都是根据文件的“上次访问时间”属性来做匹配的。
    -atime n:文件的上次访问时间是n天之前,则匹配。严格的说,n代表的其实不是“天”,而是24小时,即从现在这一刻起,往前的24*n~24*(n+1)小时之内若文件被访问过,则匹配,注意必须严格是这个区间。这里可以将n换成-n或+n,代表的范围分别是0~24*n和24*(n+1)~无穷大。
    -amin n:这里的n代表的是分钟。若文件的上次访问时间与现在时间相差的分钟数是n-1,则匹配。
    -anewer file:计算出file的mtime,若某个文件的上次访问时间在这个mtime之后,则匹配。
    类似的组合还有:-ctime、-cmin、-cnewer,这里只是把上次访问时间改成了上次更改时间;-mtime、-mmin、-newer,“上次访问时间”改成上次改动时间。这三类时间的区别参见stat命令。

  • -newerXY file/time:这是上面那些选项的集大成者。。newer后面有两个占位符,它们的值可以是a、m、c、t分别代表上次访问时间、上次modify时间、上次change时间和绝对时间;find根据Y的值来计算file的某个时间戳,然后根据X的值来做匹配。这里不举例子不行了:

    m@meng:~/tmp$ stat *
    File: ‘onlyme’
    Access: 2015-06-28 13:58:31.859458991 +0800
    Modify: 2015-06-28 12:49:16.115352584 +0800
    Change: 2015-06-28 13:58:31.859458991 +0800
    File: ‘test.sh’
    Access: 2015-06-28 13:39:07.203429170 +0800
    Modify: 2015-06-28 13:40:16.739430951 +0800
    Change: 2015-06-28 13:42:03.779433692 +0800
    m@meng:~/tmp$ find . -neweram onlyme 
    .
    ./onlyme
    ./test.sh
    m@meng:~/tmp$ find . -newerma onlyme 
    m@meng:~/tmp$
    m@meng:~/tmp$ find . -newermt "2015-06-28 13:23:16"
    .
    ./test.sh

    这里删去了一部分stat命令的输出。可以看到,同样是file=onlyme,当XY=am时,find计算出onlyme的mtime为2015-06-28 12:49:16,然后在指定目录中寻找那些atime在这个时间之后的文件,显然onlyme和test.sh的atime都符合条件;而XY=ma时,计算出onlyme的atime是2015-06-28 13:58:31,但是指定目录中任一文件的mtime都在这个时间之前,所以没有匹配。
    最后一个例子,令Y=t,然后指定一个绝对时间,接着搜索指定目录中mtime在这个时间之后的文件。显然,另X=t是无意义的,所以会报错。


  • empty:若一个文件是空的,并且不是普通文件或目录,则匹配。也可以说,若某个文件使用file命令的结果是empty,则匹配。
  • -links n:若某个文件的硬链接数量是n,则匹配。

至此,test中常用的那些都介绍完了,可以看出find做文件匹配时主要基于以下几类文件属性:名称和路径;属主和属组;权限;大小、类型及inode;时间戳;其他。
下篇介绍其他杂项。

Logo

更多推荐