你真的了解find命令吗

find 命令是很常用的shell命令,本文通过下面3个常见问题来剖析find的用法

1 按照时间查找文件

2 对路径中包含空格的文件进行查找

3 除去某些目录


1 按照时间查找文件

通过使用-ctime -mtime -atime可以指定特定时间

这三个参数的使用方法是相似的,但是意义不同,使用man find来看其作用




看上去atime比较明确,但是ctime和mtime差不多,下面是这三个参数针对的具体时间。

ctime 在写入文件、更改所有者、权限或链接设置时随 Inode 的内容更改而更改的

mtime 在文件写入时会改变

atime 在读取文件或者执行文件时更改

根据man的介绍,n的写法有三种


说实话,man里这个介绍只能让用户处于似懂非懂,想写又怕写错的混沌状态中,所以有必要总结一个图表,来形象的表示这堆加加减减。

用下面的图表就可以完全搞清楚时间的表示法:

1 对于n来说,表示的是一个确定的时间段,时间跨度是1天(24小时),n表示的是远端距离当前的距离,单位是天。

2 对于+-n来说,表示的是以某个点为基准点,向某个方向偏移的区间,n表示基准点距离当前的距离,单位是天。


2 对路径中包含有空格的文件进行查找

如果单独使用find命令查找带有空格的文件或目录是没有问题的,下图中的blank name就含有空格,find可以正常使用。


若将含有空格的文件名通过管道输送给后端命令,则会出问题


后面的xargs显然把blank name切分为两个目录,分别送给了grep导致grep无法找到两个名为blank和name的目录,这跟xargs缺省的工作方式相关,xargs会将前面管道传送来的数据按照空格/换行符切分,并送给后端命令作为参数。使用man xargs可以看到其文档对此工作方式的介绍:


有下面两个方法解决这个问题

方法1 为find 添加print0,为xargs添加 -0

find . -print0 | xargs -0 grep -n 'voidccc'

这是我最喜欢的方法,在find的文档里已经有介绍,使用man find查看一下:


使用-print0指示find用null字符来代替换行符将结果输出,同时用-0告知xargs使用null来分割输入,作为参数送给grep。


方法2 使用find -exec

find -exec grep -n 'voidccc' {} \;

这样空格不会被切分,但是注意一个细节,如果只执行上面的命令,文件名不会被显示(环境为CentOS6.4 GNU grep 2.6.3),如果要显示文件名,别忘记为grep加上 -H参数

像下面这样:



3 除去某些目录

最强烈的需求大概来自于使用svn的同学需要将.svn目录从查找内容中除去,网上有很多资料介绍这种用法,但是往往含糊不清,这里必须深入分析一下。

不得不涉及到find的语法,通过man find查看


find命令格式看似复杂,有一串的可选项,其实常用的功能不需要那么多,我们可以简化为下面的样子

find [path] [expression]

这里的path很好理解,可以不写,默认值为当前目录

而expression可以是options或者tests或者actions三者之一,expression与expression之间通过operators连接,这在文档里也有叙述


在最常被用到的参数里,属于options的是下面几个,options总是返回true

-mindepth


属于tests的是下面几个,tests返回true或者false
-ctime

-wholename

-size


属于actions的是下面几个,actions有副作用,返回true或者false,对于prune来说,永远返回true。
-print

-prune

而operators可以是下面几个,其中简单版本(! -o -a)都是POSIX compliant,看来POSIX标准的制定者非常为开发人员着想,把标准都设计成最简单形式了。operators的一个重要特性是支持短路运算,我们也正是利用了这个特性。

取反:

! expr1 //POSIX compliant

-not expr1 //等效于第一个

与运算:

erpr1 -a expr2 //POSIX compliant

erpr1 -and expr2 //等效于第一个

expr1 expr2 //不写等效于第一个

或运算:

expr1 -o expr2

expr1 -or expr2

研究清楚了find的基本用法,可以给出答案,使用下面3种写法都可以达到目的

find -wholename "*.svn" -prune -o -print
find -wholename "*.svn*" -o -print
find ! -wholename "*.svn*"

依次拆解这三条命令,添加了红色的[]代表一个expression,蓝色代表被我补齐的内容,expr1,2,3是为了后续叙述方便

find [-wholename "*.svn*"]-a[-prune] -o[-print]// expr1 -a expr2 -o expr3

find [-wholename "*.svn*"]-o[-print]// expr1 -o expr2

find ! [-wholename "*.svn*"]-a[-print]// !expr1 -a expr2

命令1,如果路径包含.svn,则expr1 为true,导致expr2执行判断,但是expr2永远为true,所以expr1 -a expr2已经为true,所以短路规则-print不会被执行,此路径无法被打出来,过滤掉了。如果包含.svn,则expr1为false,这导致expr1 -a expr2为false,所以必须执行后面的-print,所以该路径被打印出来。

命令2,如果路径包含.svn,则expr1为true,直接短路掉-print,路径无法打印,如果路径不包含.svn,则expr1为false,导致expr2被执行,则路径打印。

命令3,没有写任何actions,则系统补全一个-print,而这个-print和前面是-a关系,所以如果路径包含.svn,则expr1为true,!expr1为false,导致-print被短路,路径不被打印。如果路径未包含.svn,则expr1为false,!expr1为true,则-print需要被执行导致路径被打印。


综合分析,上面三条语句都可以达到忽略路径中的.svn文件/目录的功能,一个稍显奇怪的地方是-prune反而变的可有可无。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值