bash&shell系列文章:http://www.cnblogs.com/f-ck-need-u/p/7048359.html
find用于搜索文件或目录,功能非常强大。该工具是findutils包提供的,该包中还包括一个老版本的oldfind工具,以及另一个非常强大的xargs命令,在搜索文件时,如果还要对搜索的文件进行后续的处理,一般都会结合xargs来实现。但本文并不过多涉及xargs,如果要了解xargs用法,见我的另一篇关于xargs的总结xargs的原理剖析及用法详解,目前在网上暂时还没找到比这篇文档更详细的xargs说明。
find搜索是从磁盘进行指定目录开始扫描,而不是从数据库搜索。
find [path...] [expression_list]
1.1 find基础示例
因为内容有点多,所以放进单独一篇文章中了。参见find常用用法示例,建议在阅读本文之前一看,相信其中一些用法不会让你失望的。
1.2 find理论部分
find [path...] [expression_list]
expression分为三种:options、test、action。对于多个表达式,find是从左向右处理的,所以表达式的前后顺序不同会造成不同的搜索性能差距。
find首先对整个命令行进行语法解析,并应用给定的options,然后定位到搜索路径path下开始对路径下的文件或子目录进行表达式评估或测试,评估或测试的过程是按照表达式的顺序从左向右进行的(此处不考虑操作符的影响),如果最终表达式的表达式评估为true,则输出(默认)该文件的全路径名。
对于find来说,一个非常重要的概念:find的搜索机制是根据表达式返回的true/false决定的,每搜索一次都判断一次是否能确定最终评估结果为true,只有评估的最终结果为true才算是找到,并切入到下一个搜索点。
1.2.1 expression operators
操作符控制表达式运算方式。确切的说,是控制expression中的options/tests/actions的运算方式,无论是options、tests还是actions,它们都可以给定多个,例如find /tmp -type f -name "*.log" -exec ls '{}' ; -print,该find中给定了两个test,两个action,它们之间从前向后按顺序进行评估,所以如果想要改变运算逻辑,需要使用操作符来控制。
注意,理解and和or的评估方式非常重要,很可能写在and或or后面的表达式不起作用,而导致跟想象中的结果不一样。
下面的操作符优先级从高到低。
------------------------------------------------------------------------------------
( expr ) :优先级最高。为防止括号被shell解释(进入子shell),所以需要转义,即(...)
------------------------------------------------------------------------------------ ! expr :对expr的true和false结果取反。同样需要使用引号包围
------------------------------------------------------------------------------------ -not expr :等价于"! expr"
------------------------------------------------------------------------------------ expr1 expr2 :等同于and操作符。
------------------------------------------------------------------------------------ expr1 -a expr2 :等同于and操作符。
------------------------------------------------------------------------------------ expr1 -and expr2 :首先要求expr1为true,然后expr2以expr1搜索的结果为基础继续检测,然后再返回
:检测值为true的文件。因为expr2是以expr1结果为基础的,所以如果expr1返回
:false,则expr2直接被忽略而不会进行任何操作
------------------------------------------------------------------------------------ expr1 -o expr2 :等同于or操作符
------------------------------------------------------------------------------------ expr1 -or expr2 :只有expr1为假时才评估expr2。
------------------------------------------------------------------------------------ expr1 , expr2 :逗号操作符表示列表的意思,expr1和expr2都会被评估,但expr1的true或false是被
:无视的,只有expr2的结果才是最终状态值。
关于and和or操作符,一定要明确的是and后的表达式操作的对象是前面表达式的结果,而or操作符的对象则是前面评估后为假时文件。
例如:
它是一个and操作符,-name表达式是在-type筛选的结果基础上再匹配文件名的。但如果是:
则-type f返回真的文件直接执行它后面默认的-print,而不满足-type f的才会去被-name评估,所以返回结果中即有任意普通文件,也有任意log文件,但两者同名的文件只返回一次。
总之,and和or对于find的影响比较大,后面会专门用一节彻底搞懂operator和action来详细解释。另外,在后文find深入用法示例中还有一个关于"忽略目录"的例子,它很好的解释了操作符的行为。
1.2.2 expression-options
options总是返回true。除了"-daystart",options会影响所有指定的test表达式部分,哪怕是test部分写在options的前面。这是因为options是在命令行被解析完后立即处理的,而test是在检测到文件后才处理的。对于"-daystart"这个选项,它们仅仅影响写在它们后面的test部分,因此,建议将任何options部分写在expression的最前面,若不如此,会给出一个警告信息。
---------------------------------------------------------------------------------------------------
-daystart:指定以每天的开始(凌晨零点)计算关于天的时间,用于改变时间类(-amin,-atime,-cmin,-ctime,-mmin和-mtime)
:的计算方式。默认天的计算是从24小时前计算的。例如,当前时间为5月3日17:00,要求搜索出2天内修改过的文件,默认
:搜索文件的起点是5月1日17:00,如果使用-daystart,则搜索文件的起点是是5月1日00:00。
:注意,该选项只会影响写在它后面的test表达式。
--------------------------------------------------------------------------------------------------- -depth :搜索到目录时,先处理目录中的文件(子目录),再处理目录本身。对于"-delete"这个action,它隐含"-depth"选项。
--------------------------------------------------------------------------------------------------- -maxdepth levels:指定tests和actions作用的最大目录深度,只能为非负整数。可以简单理解为目录搜索深度,但并非如此。当
:前path目录的层次为1,所以若指定-maxdepth 0将得不到任何结果。
--------------------------------------------------------------------------------------------------- -mindepth levels:tests和actions不会应用于小于指定深度的目录,"-mindepth 1"表示应用于所有的文件。
--------------------------------------------------------------------------------------------------- -ignore_readdir_race:当无法用stat检测文件信息时(如无权限)会给出下图所示的错误信息,如要忽略该信息,可以使用该选项。
------------------------------------------------------------------------------------------------
-warn:忽略警告信息。
1.2.3 expression-tests
find解析完命令行语法之后,开始搜索文件,在搜索过程中,每次检测到的文件都会被test expression进行测试,符合条件的将被保留。
数值部分可以设置为以下3种模式:n可以是小数。
+n:大于n
-n:小于n
n :精确的等于n
对于文件大小而言,文件的大小是精确的,指定100KB,则比对的值必定是100KB。此时(+ -)n和字面意思是一样的。
但对于时间而言,时间是有时间段的,例如指定前第四天,第四天也整整占用了一天,所以(+ -)n和文件大小的计算方法是不一样的。
find在计算以天数为单位的时间时,默认会转换为24小时制,除非同时指定了"-daystart"这个选项,这在前面已经解释过了。例如当前时间为5月3号17:00,那么计算"atime +1"的时候,真正计算的是24*1=24小时之前,又因为前48小时到前24小时之间都属于前一天内,所以其搜索的是是5月1号17:00以前被访问过,若指定了"-daystart",则计算的是5月2号00:00之前被访问过。
具体的选项如下:
-type X:根据文件类型来搜索
· b:块设备文件
· c:字符设备文件
· d:目录
· p:命名管道文件(FIFO文件)
· f:普通文件
· l:符号链接文件,即软链接文件
· s:套接字文件(socket)
【文件大小或内容类测试条件】
-size n[cwbkMG]:根据文件大小来搜索,可以是(+ -)n,单位可以是: · b:512字节的(默认单位) · c:1字节的 · w:2字节 · k:1024字节 · M:1024k · G:1024M empty:空文件,对于目录来说,则是空目录
【文件名或路径名匹配类测试条件】
------------------------------------------------------------------------------------------------ -name pattern | 文件的basename(不包括其前导目录的纯文件名)能被通配符模式的pattern匹配到。由于前导目录被移除,
| 所以find对包含"/"的pattern是绝对不可能匹配到内容的,例如"-name a/b"的结果一定是空且会给出 | 警告信息,若要匹配这样的文件,可考虑使用"-path"或"-name b"。需要注意的是,在find中的通配元
| 字符"*"、"?"和"[]"是能够匹配以点开头的文件的,之所以要在此说明这一点,是因为在bash中,这些通 | 配元字符默认是无法匹配"."开头的文件的,例如"cp ~/* /tmp"不会把隐藏文件也拷贝走。若要忽略一个
| 目录及其内的文件,可以配合"-prune",它会跳过整个目录而不对此目录做任何检查。注意pattern要用 | 引号包围防止被shell解释 ----------------|------------------------------------------------------------------------------- -iname pattern | 不区分大小的"-name" ----------------|------------------------------------------------------------------------------- -path pattern | 文件名能被通配符模式的pattern匹配到。此模式下,通配元字符"*"、"?"和"[]"不认为字符"/"或"."是
| 特殊字符,也就是说这两个字符也在通配范围内,所以能匹配这两个字符。例如find . -path "./sr*sc" | 可以匹配到名为"./src/misc"的目录。find会将"-path"的pattern与文件的dirname和basename的结
| 合体进行比较,由于dirname和basename的结合体不包含尾随"/",所以如果pattern中指定了尾随"/"是 | 不可能匹配到任何东西的,例如find /tmp -path "/tmp/ab*/",实际上它会给出警告信息,提示
| pattern以"/"结尾。使用"-path"的时候,一定要注意"-path"后指定的路径起点属于
| "find path expression"的path内,例如"find /bar -path /foo/bar/myfile -print"不可能 | 匹配到任何东西。若要忽略目录及其内文件,可配合"-prune",它会跳过整个目录而不对此目录做检查。如
| "find . -path ./src/emacs -prune -o -print"将跳过对目录"./src/emacs"的检查。
| 注意pattern要用引号包围防止被shell解释 ----------------|--------------------------------------------------------------------------------- -ipath pattern | 不区分大小写的"-path" ----------------|--------------------------------------------------------------------------------- -regex pattern | 文件名能被正则表达式pattern匹配到的文件。正则匹配会匹配整个路径,例如要匹配文件名为"./fubar3"
| 的文件,可以使用".*bar."或".*b.*3",但不能是"f.*r3",默认find使用的正则类型是Emacs正则,
| 但可以使用-regextype来改变正则类型 ----------------|--------------------------------------------------------------------------------- -iregex pattern | 不区分大小写的"-regex"
【权限类测试条件】
-perm mode | 精确匹配给定权限的文件。"-perm g=w"将只匹配权限为0020的文件。当然,也可以写成三位数字的权限模式
-----------|------------------------------------------------------------------------------------- -perm -mode| 匹配完全包含给定权限的文件,这是最可能用上的权限匹配方式。例如给定的权限"-0766",则只能匹配"N767"、 | "N777"和"N776"这几种权限的文件,如果使用字符模式的权限,则必须指定u/g/o/a,例如"-perm -u+x,a+r"
| 表示至少所有人都有读权限,且所有者有执行权限的文件
-----------|------------------------------------------------------------------------------------- -perm /mode| 匹配任意给定权限位的权限,例如"-perm /640"可以匹配出600,040,700,740等等,只要文件权限的任意位能
| 包含给定权限的任意一位就满足
-----------|------------------------------------------------------------------------------------- -perm +mode| 由于某些原因,此匹配模式被替换为"-perm /mode",所以此模式已经废弃
-----------|------------------------------------------------------------------------------------- -executable| 具有可执行权限的文件。它会考虑acl等的特殊权限,只要是可执行就满足。它会忽略掉-perm的测试
-----------|------------------------------------------------------------------------------------- -readable | 具有可读权限的文件。它会考虑acl等的特殊权限,只要是可读就满足。它会忽略掉-perm的测试
-----------|------------------------------------------------------------------------------------- -writable | 具有可写权限的文件。它会考虑acl等的特殊权限,只要是可写就满足。它会忽略掉-perm的测试(不是writeable)
【所有者所属组类测试条件】
-gid n :gid为n的文件 -group gname:组名为gname的文件 -uid n :文件的所有者的uid为n -user uname :文件的所有者为uname,也可以指定uid -nogroup :匹配那些所属组为数字格式的gid,且此gid没有对应组名的文件 -nouser :匹配那些所有者为数字格式的uid,且此uid没有对应用户名的文件
【时间戳类测试条件】
-anewer file:atime比mtime更接近现在的文件。也就是说,文件修改过之后被访问过 -cnewer file:ctime比mtime更接近现在的文件 -newer file:比给定文件的mtime更接近现在的文件。 -newer[acm]t TIME:atime/ctime/mtime比时间戳TIME更新的文件 -amin n:文件的atime在范围n分钟内改变过。注意,n可以是(+ -)n,例如-amin +3表示在3分钟以前 -cmin n:文件的ctime在范围n分钟内改变过 -mmin n:文件的mtime在范围n分钟内改变过 -atime n:文件的atime在范围24*n小时内改变过 -ctime n:文件的ctime在范围24*n小时内改变过 -mtime n:文件的mtime在范围24*n小时内改变过 -used n:最近一次ctime改变n天范围内,atime改变过的文件,即atime比ctime晚n天的文件,可以是(+ -)n
【软硬链接类测试条件】
-samefile name:找出指定文件同indoe的文件,即其硬链接文件 -inum n:inode号为n的文件,可用来找出硬链接文件。但使用"-samefile"比此方式更方便 -links n:有n个软链接的文件
【杂项测试】
-false:总是返回false,这选项有奇用 -true :总是返回true,这选项有奇用
1.2.4 expression-actions
actions部分一般都是执行某些命令,或实现某些功能。这部分是find的command line部分。
-delete | 删除文件,如果删除成功则返回true,如果删除失败,将给出错误信息。"-delete"动作隐含"-depth"。
--------------------------------------------------------------------------------------------------- -exec command ;| 注意有个分号";"结尾,该action是用于执行给定的命令。如果命令的返回状态码为0则该action返回true。
| command后面的所有内容都被当作command的参数,直到分号";"为止,其中参数部分使用字符串"{}"时,它
| 表示find找到的文件名,即在执行命令时,"{}"会被逐一替换为find到的文件名,"{}"可以出现在参数中的
| 任何位置,只要出现,它都会被文件名替换。 | 注意,分号";"需要转义,即";",如有需要,可以将"{}"用引号包围起来
---------------|----------------------------------------------------------------------------------- -ok command ; | 类似于-exec,但在执行命令前会交互式进行询问,如果不同意,则不执行命令并返回false,如果同意,则执 | 行命令,但执行的命令是从/dev/null读取输入的
---------------|----------------------------------------------------------------------------------- -print | 总是返回true。这是默认的action,输出搜索到文件的全路径名,并尾随换行符" "。由于在使用"-print"时所有的结
| 果都有换行符,如果直接将结果通过管道传递给管道右边的程序,应该要考虑到这一点:文件名中有空白字符(换行符、制表
| 符、空格)将会被右边程序误分解,如文件"ab c.txt"将被认为是ab和c.txt两个文件,如不想被此分解影响,可考虑使
| 用"-print0"替代"-print"将所有换行符替换为"