find详解

Linux系统有两个命令可用于查找文件系统上符合查询条件的文件:
  • locate

  • find

1. locate命令  

  locate命令主要用于查找系统文件,其工作特性如下:

  • 查找速度快。locate命令是基于linux的文件系统之外构建的一个文件目录索引数据库进行文件查询的,即locate命令是依赖于事先构建好的索引库,而非直接遍历linux系统下的所有文件来进行文件查找的。

  • 模糊查找。如果不加任何参数进行搜索,只要包含搜索字符的目录、文件都会全部搜索出来。

  • 非实时查找。locate的索引数据库是由updatedb程序来进行更新,并由cron daemon建立周期性的更新任务,而非实时更新。更新时间一般是晚上。如果是当天建立或者更新的文件,由于未到周期性更新任务执行时间,故用locate命令一般是无法查找到该文件的。

另有两点需要注意,一是locate命令的索引数据库文件在“/var/lib/mlocate”目录下,可能不同版本的linux系统位置会有所不同。二是updatedb命令索引构建过程需要遍历整个根文件系统,极消耗资源。   

1.1. 命令格式

locate [参数]... PATTERN...

1.2. 常用选项

"locate -c",表示对搜素出来的结果记录总数进行统计
示例:locate -c sh*
搜索当前目录所有sh开头的文件数量

"locate -b",表示只匹配路径中的基名包含搜索字符的文件

"locate -r",表示使用正则表达式做查找的条件
示例:[root@host~]# locate -r makefile$
查找以makefile结尾的文件

2. find命令

  find是一个实时查找文件工具,通过遍历指定起始路径下文件系统层级结构完成文件的查找。Linux下find命令提供了相当多的查找条件,功能很强大。但在运行一个非常消耗资源的find命令时,最好把它放在后台执行,因为遍历一个大的文件系统可能会话费很长的时间。

2.1. 命令格式

find [OPTIONS] [查找起始路径] [查找条件] [处理动作]

  • 查找起始路径:指定具体搜索目标起始路径,默认为当前目录;

  • 查找条件:指定的查找标准,可以根据文件名、大小、类型、从属关系、权限等等标准进行,默认为找出指定路径下的所有文件;

  • 处理动作:对符合查找条件的文件作出的操作,例如删除等操作,默认为输出至标准输出。

2.2. 查找条件

查找条件的表达式一般分为两种,分别是选项和测试。

测试:结果通常为布尔型("true","false") 1)组合测试:

  • 与:-a,只有所有均符合条件,其结果才为真。为默认的查找条件之间的组合逻辑。

  • 或:-o,只要有一个符合条件,其结果即为真。

  • 非:-not,查找条件取反。

示例1:find /tmp -not -user root -type f
找出/tmp目录下属主为非root的所有文件;

示例2:find /tmp -not -name "fstab" -type f
找出/tmp目录下文件名中不包含fstab字符串的文件,默认查找条件组合为-a。

示例3:find /tmp -not -user root -a -not -name "fstab" -type f
找出/tmp目录下属主为非root,而且文件名不包含fstab字符串的文件。 该示例还可以用这样的命令达到一样的效果:find /tmp -not \( -user root -o iname "fstab" \) -a -type f
 其中括号旁边的两个反斜杠\是用于转义的。而其中的组合逻辑如下:
  !A -a !B = !(A -o B)
  !A -o !B = !(A -a b)    选项一般有以下几种类型: 1). 根据文件名查找

  • -name "pattern"
     查找匹配pattern内容的文件
     示例:find /etc -name "shadow"
     查找/etc目录下文件名为shadow的文件;

  • -iname "pattern"
     匹配pattern的文件名忽略大小写
     示例:find /tmp -iname "test"
     查找/tmp目录下文件名为test,且忽略大小写的文件

在这里,有两点需要注意下:

  • 根据文件名查找支持glob风格的通配符,而不是正则表达式的通配符。
    示例:find /etc/ -name “sh*”
    查找/etc路径下以sh开头的文件。

  • 查找条件为-regex pattern时:基于正则表达式模式查找文件,匹配的是整个路径,而非仅仅要查找的文件名。而实际上用locate命令也可以达到一样的目的,查找路径效率会更高,查找速度更快,所以此查找条件参数一般用的不多。

2). 根据文件从属关系查找

  • -user USERNAME  查找属主为指定用户的所有文件或目录;

     示例:find /tmp -user root

  • -group GRPNAME  查找属组为指定组的所有文件或目录;

     示例:find /tmp -group gentoo

  • -uid UID  查找属主为指定UID的所有文件或目录;

     示例:find /etc/ -uid 3000

  • -gid GID  查找属组为指定GID的所有文件或目录;

     示例:find /tmp -gid 4001

  • -nouse  查找没有属主的文件或目录;
     示例:find /tmp -nouser
      查找/tmp目录下没有属主的文件或目录

  • -nogroup
     查找没有属组的文件或目录;
     示例:find /tmp -nogroup
      查找/tmp目录下没有属组的文件或目录

3). 根据文件的类型查找

-type TYPE
  f:普通文件
  d:目录文件
  l:符号链接文件
  b:块设备文件
  c:字符设备文件
  p:管道文件
  s:套接字文件

示例1:find /etc -name "sh*" -type f
 查找/etc目录下文件名以sh开头的文件

示例2:find /etc -type l
 查找/etc目录下的所有符号连接文件

示例3:find /dev -type c
 查找/dev目录下的所有字符设备文件

4). 根据文件大小查找
格式为:-size [+|-] #Unit,在这里#表示一个数值,Unit表示单位
常用单位:K,M,G,默认单位是字节

#Unit:表示size的大小范围是(#-1,#],比如 -size 4M,表示要查找的文件大小是3M<文件≤4M

-#Unit:表示size的大小范围是[0,#-1],比如 -size -4M,表示要查找的文件大小是0M≤文件≤3M

+#Unit:表示size的大小范围是(#,oo),比如 -size +4M,表示要查找的文件大小是文件>4M

5). 根据时间戳查找

以“天”为单位:
格式为:
-atime [+|-]# ,表示在指定时间曾被存取过的文件,意思是文件被读取过;
-mtime [+|-]# ,表示在指定时间曾被改动过的文件,意思是文件内容被更改过;
-ctime [+|-]# ,表示在指定时间曾被更改过的文件,意思是文件权限被更改过;

注意点:

  • #表示一个单位24小时。

  • 时间是以24小时为一个单位。要注意查找的时间是以当前执行命令的时间点作为起始点往回推的。

  • 举例在2017年5月20日 12:00时间点开始找指定时间范围的文件。
    #:比如-atime 3,表示找出第3天被访问过的文件,时间范围是2017/05/16 12:00~2017/05/17 12:00,(即从执行命令时间点开始往前推的72~96小时)
      示例:方法一: find /var/log/ -atime 3 -type f -print       方法二:find /var/log/ -atime +2 -mtime -4 -type f -print

-#:比如-atime -3,表示找出3天内被访问过的文件,时间范围是2017/05/17 12:00~2017/05/20 12:00,(即从执行命令时间点开始往前推的0~72小时)
  示例:find /var/log/ -atime -3 -type f -print   

+#:比如-atime +3,表示找出3天以前被访问过的文件,时间范围是2017/05/17 12:00之前,(即从执行命令时间点开始往前推的>72小时)
  示例:find /var/log/ -atime +3 -type f -print

其他示例:
示例1:find / \( -nouser -o -nogroup \) -atime -7 -ls
  查找当前系统上没有属主或者属组,且最近一周内曾被访问过的文件或目录。

示例2: find /etc -mtime -7 -a not \( -user root -o -user hadoop \) -ls
  查找/etc目录下最近一周内其内容修改过,且属主布斯和root用户也不是hadoop用户的文件或目录

-atime -mtime -ctime的用法类似。

以“分钟”为单位:
格式为:
-amin [+|-]#
-mmin [+|-]#
-cmin [+|-]#
用法与以“天”为单位的用法一致,只是单位变为分钟数。

6). 根据权限查找
格式为:-perm [/|-]mode
  mode:表示精确权限匹配;
  /mode:表示任何用户(user,group,other)的权限中的任何一位(r,w,x)符合条件即满足。9位权限之间存在“或”关系;
  -mode:表示每一类用户(user,group,other)的权限中的每一位(r,w,x)同时符合条件即满足。9位权限之间存在“与”关系;

示例1:find ./ -not -perm -222 ls
表示查找当前目录下至少有一类用户是没有w权限的文件

示例2:find ./ -perm -222 -ls
表示查找当前目录下每一类用户都有w权限的文件

2.3. 处理动作

  • -print: 输出至标准输入,为默认的动作;

  • -ls: 类似于对查找到的文件执行 “ls -l”命令,输出文件的详细信息;

  • -delete:删除查找到的文件;

  • fls /PATH/TO/SOMEFILE:把查找到的所有文件的长格式信息保存至指定文件中;

  • -ok COMMAND {} \;:对查找到的每个文件执行由COMMAND表示的命令;每次操作都由客户进行确认;花括号表示引用查找到的文件;
    示例:find ./ -nouser -a -nogroup -ok chown root:root {} \;
    表示查找当前目录下没有属主且没有属组的文件,并将其属主和属组均修改为root.`

  • -exec COMMAND {} \;:对查找到的每个文件执行由COMMAND表示的命令;与-ok COMMAND {} /;有所区别的是,每次操作都不再需要客户进行确认。
    示例:find ./ -perm /002 -exec mv {} {}.danger \;
    表示查找当前目录下other用户有w权限的文件,并将其改名为原名.danger

注意:find传递查找到的文件路径至后面的命令时,是先查找出所有符合条件的文件路径,并一次性传递给后面的命令;但是有些命令不能接受过长的参数,此时命令执行会失败;以下方式可规避此问题。

  • find | xargs COMMAND

      在使用find命令的-exec选项处理匹配到的文件时, find命令将所有匹配到的文件一起传递给exec执行。但有些系统对能够传递给exec的命令长度有限制,这样在find命令运行几分钟之后,就会出现溢出错误。错误信息通常是“参数列太长”或“参数列溢出”。这就是xargs命令的用处所在,特别是与find命令一起使用。

      find命令把匹配到的文件传递给xargs命令,而xargs命令每次只获取一部分文件而不是全部,不像-exec选项那样。这样它可以先处理最先获取的一部分文件,然后是下一批,并如此继续下去。

      在有些系统中,使用-exec选项会为处理每一个匹配到的文件而发起一个相应的进程,并非将匹配到的文件全部作为参数一次执行;这样在有些情况下就会出现进程过多,系统性能下降的问题,因而效率不高;

      而使用xargs命令则只有一个进程。另外,在使用xargs命令时,究竟是一次获取所有的参数,还是分批取得参数,以及每一次获取参数的数目都会根据该命令的选项及系统内核中相应的可调参数来确定。

示例:find /tmp -perm /002 -type f | xargs chmod o-w
查找/tmp下具有w权限的other用户,并将其去写权限