find&正则表达式
标准的正则表示式格式
常用元字符
代码 | 说明 |
---|---|
. | 匹配除换行符以外的任意字符 |
\w | 匹配字母或数字或下划线 |
\s | 匹配任意的空白符 |
\d | 匹配数字 |
\b | 匹配单词的开始或结束 |
^ | 匹配字符串的开始 |
$ | 匹配字符串的结束 |
常用限定符
代码/语法 | 说明 |
---|---|
* | 重复零次或更多次 |
+ | 重复一次或更多次 |
? | 重复零次或一次 |
重复n次 | |
重复n次或更多次 | |
重复n到m次 |
常用反义词
代码/语法 | 说明 |
---|---|
\W | 匹配任意不是字母,数字,下划线,汉字的字符 |
\S | 匹配任意不是空白符的字符 |
\D | 匹配任意非数字的字符 |
\B | 匹配不是单词开头或结束的位置 |
[^x] | 匹配除了x以外的任意字符 |
[^aeiou] | 匹配除了aeiou这几个字母以外的任意字符 |
扩展的正则表达式 相对标准的正则表达式 在次数表示的方面只是少了\
其他都一样
find
支持的正则语法有emacs、posix-awk、posix-basic、posix-egrep、posix-extended
他默认使用的是emacs
风格而非我们常用的posix-egrep and posix-extended
格式的正则表达式
创建测试环境(模拟生产应用日志)
root@王初一:~# mkdir logs
root@王初一:~# mkdir logs/2019{01..12}{01..30}
root@王初一:~# rm -rf logs/201902{29..30}
root@王初一:~# for i in $(ls ~/logs/); do cd logs/$i; echo "$i TIME add logs" > access.log; touch -mt "$i"0302 access.log; cd ../..; done
find使用语法
find path -option [ -print ] [ -exec -ok command ] {} \;
查找30天之前的文件
find
正则表达式是匹配全路径(包含文件名和相对路径),它不是搜索。也就是说所写的正则必须能够匹配整个搜索目录路径和文件名的组合,而不是单单匹配文件名本身
root@王初一:~# find ~/logs/ -mindepth 1 -maxdepth 2 -regextype posix-extended -regex ".+\.log$" -user root -mtime +30 -type f
比如:查找当前目录下的access.log
文件。 它的全路径(相对路径)是 ./access.log
, 注意前面有./
而不只是文件名。所以正则可以写成: .*log
; 或者.*cess.*
等等,但是不能写成ac.*og
(这里就不能匹配前面的目录部分)
参数说明:
-maxdepth | #最大深度 |
---|---|
-mindepth | #最小深度 |
-regextype | #调用正则 |
-posix-extended | #使用正则格式 |
-user | #匹配属主 |
-mtime | #匹配时间 |
-type | #匹配文件类型 |
匹配时间参数说明
atime 为 access time 即文件被读取或者执行的时间,修改文件是不会改变access time的。
ctime 为 change time 文件状态改变时间,指文件的i结点被修改的时间,如通过chmod修改文件属性,ctime就会被修改。
mtime 为 modify time 指文件内容被修改的时间。
-mtime -n +n 按文件更改时间来查找文件,-n指n天以内,+n指n 天之前
-atime -n +n 按文件访问时间来查找文件,-n指n天以内,+n指n 天之前
-ctime -n +n 按文件创建时间来查找文件,-n指n天以内,+n指n 天之前
-cmin -n +n 按文件修改时间来查找文件,-n值n分钟以内,+n值n分钟之前
定时清理脚本
root@王初一:~# vim clear_oldlog.sh
#!/bin/bash
LOG_PATH="/root/logs/"
if [ -d $LOG_PATH ]; then
find $LOG_PATH -mindepth 1 -maxdepth 2 -regextype posix-extended -regex ".+\.log$" -user root -mtime +30 -type f | xargs rm -rf >/dev/null 2>&1
fi
其他使用方法
-path
模糊匹配路径
root@王初一:~# find . -path '*20190326/access.log'
./logs/20190326/access.log
-prune
-prune
是一个动作项,它表示当文件是一个目录文件时,不进入此目录进行搜索。
要理解-prune
动作,首先得理解find命令的搜索规则(也可以说find
命令的算法)。
find
命令递归遍历所指定的目录树,针对每个文件依次执行find
命令中的表达式,表达式首先根据逻辑运算符进行结合,然后依次从左至右对表达式求值。以下面代码为例,进行说明
find PATHP1 OPT1 TEST1 ACT1 ( TEST2 or TEST3 ) ACT2
(1) 根据OPT1设置项进行find命令的整体设置,若没有-depth设置项,依次进行下面的步骤
(2) 令文件变量File = PATHP1
(3) 对File文件进行TEST1测试,若执行结果为false,转(8)
(4) 对File文件进行ACT1动作,若执行结果为false,转(8)
(5) 对File文件进行TEST2测试,若执行结果为true,转(7)
(6) 对File文件进行TEST3测试,若执行结果为false,转(8)
(7) 对File文件进行ACT2动作
(8) 若File文件是一个目录,并且没有被执行过-prune动作,则进入此目录
(9) 当前目录下是否还有文件,若有依次取一个文件,令File指向此文件,转(3);
(10) 判断当前目录是否是PATHP1,若是则程序退出;若不是,则返回上一层目录,转(9)
理解了上面的流程,那么不难理解下面的代码为什么只输出一个'.'
$ find . -prune
.
再有,当前目录下大于4090字节的文件有两个,而大于4096字节的文件只有一个,如下:
$ find . -size +4090c -print
.
./a_book_of_c.chm
$ find . -size +4096c -print
./a_book_of_c.chm
那么,将上面两个-print
都替换为-prune
,这两条命令分别输出什么?
$ find . -size +4090c -prune
.
$ find . -size +4096c -prune
./a_book_of_c.chm
-prune
经常和-path
或-wholename
一起使用,以避开某个目录,常见的形式是:
$ find PATH (-path <don't want this path #1> -o -path <don't want this path #2>) -prune -o -path <global expression for what I do want>
注意:如果同时使用-depth
设置项,那么-prune
将被find
命令忽略。man手册页中这么说:"If -depth is given, false; no effect."
说到这里,又得说说-depth
设置项。网上好多资料说-depth
设置项的功能是“在查找文件时,首先查找当前目录中的文件,然后再在其子目录中查找”,这明显是错误的,man手册页中如是说:"-depth Process each directory's contents before the directory itself."
。这有点像树的后序遍历,先遍历当前节点的所有子节点,然后再访问当前节点...
时间戳
文件有三个时间属性:创建时间、最近修改时间、最近访问时间。
最近修改时间又包括两种,一是文件的状态(也即权限如rwx
等)最近被修改时间,一是文件的数据(也即内容)最近被修改时间。touch
命令改变的即是文件数据最近被修改时间。
最近访问时间,指的是最近一次文件数据(内容)被访问的时间。因此,使用ls
命令输出文件的相关信息并不会修改文件的最近访问时间。
find
命令提供了针对文件的最近访问时间、文件状态最近被修改时间、文件数据最近被修改时间进行匹配的测试项,分别是-amin
, -cmin
, -mmin
和-atime
, -ctime
, -mtime
两组,第一组基于分钟,第二组基于天。
以-amin
为例,假设当前时间tnow="2007-11-12 14:42:10"、t1="2007-11-12 14:39:10"、t2="2007-11-12 14:40:10"
,那么要查找最近访问时间属于[t1,t2]
时间段的文件,可以这么写:
$ find . -amin 3
若测试项参数是数字,则基本上都可以在数字参数前加"+"或者"-"号,表示“大于”或“小于”的意思,因此,要查找最近访问时间属于[t1,tnow]时间段的文件,可以这么写:
$ find . -amin -3
"-amin n"
和"-atime n"
的处理方法都是:根据当前时间和文件的相应时间属性求n值,然后比较n值和参数n,看是否符合要求。但是这个求n值的过程却有很大不同,他们的不同也代表了两组(基于分钟和基于天)的不同:
"-amin n"
1、求Δt,用当前时间减去文件对应属性的时间值即得到Δt,Δt = tnow - tfile;
2、求浮点数f,用Δt除以1分钟,f = Δt / 1min;
3、将f的小数部分入到整数部分,得到n。即,不管f是6.0102还是6.8901,n都等于7
"-atime n"
1、求Δt,用当前时间减去文件对应属性的时间值即得到Δt,Δt = tnow - tfile;
2、求浮点数f,用Δt处以24小时,f = Δt / 24hours;
3、将f的小数部分都舍掉,得到n。即,不管f是6.0102还是6.8901,n都等于6
权限位
用windows
,从windows
系统拷过来的文件经常被加上了可执行权限,比如我现在想把主目录下所有的后缀名为.txt
.pdf
.rm
并且具有可执行权限位的文件查找出来
权限位测试项:-perm
。-perm
支持符号权限位表示法也支持绝对(八进制)权限位表示法,但是最好使用八进制的权限表示法
-perm
基本上有下面这几中形式:
-perm mode File's permission bits are exactly mode.
-perm -mode All of the permission bits mode are set for the file.
-perm /mode Any of the permission bits mode are set for the file.
-perm +mode (此形式已经不推荐使用,功能与/mode相同)
文件类型
有一个问题:我只想查找符号连接文件,可是查找结果中却包括了普通文件、目录文件等等,不相关的东西太多了,怎么把不是符号连接文件的查找结果去掉?
-type
测试项刚好可以满足你的要求,-type c
即可,其中c
表示文件类型,find
中支持如下类型:
b block (buffered) special
c character (unbuffered) special
d directory
p named pipe (FIFO)
f regular file
l symbolic link;
s socket
D door (Solaris)
针对上面的问题,可以这么写:
$ find . -name "e100*" -type l -print
./e1000
./e100puk.txt
但是,不要这么写:
$ find -L . -name "e100*" -type l -print
加上'-L'选项之后,你将查不到需要的东西,除非符号连接已经失效了。
文件大小
-size
测试项根据文件的大小查找文件,文件大小既可以用块(block)
来计量,也可以用字节来计量。默认情况下以块计量文件大小,若想使用字节来计量只需要在数字参数后加c即可。find
支持的其他计量方式有:
-size n[cwbkMG],分别表示
‘b’ for 512-byte blocks (this is the default if no suffix is used)
‘c’ for bytes
‘w’ for two-byte words
‘k’ for Kilobytes (units of 1024 bytes)
‘M’ for Megabytes (units of 1048576 bytes)
‘G’ for Gigabytes (units of 1073741824 bytes)
用户、用户组
根据用户、用户组来查找文件,这个没有太多要说的,记住命令格式即可:
-uid n
-user username or uid
-nouser
-gid n
-group gname or gid
-nogroup
输出格式
如果你不想查找到你想要的文件事单调的输出文件名,你可以使用-printf
动作项输出你想要的格式,下面举几个-printf
动作的参数:
%p 输出文件名,包括路径名
%f 输出文件名,不包括路径名
%m 以8进制方式输出文件的权限
%g 输出文件所属的组
%h 输出文件所在的目录名
%u 输出文件的属主名
执行外部命令
这又是一个很容易出彩的地方。find
真是强大,对查找到的文件竟然可以调用外部命令进行处理。-exec
动作项就是来完成这个功能的,格式是:
find . EXPR1 -exec command {} /;
注意:后一个花括号'}'和'/'之间有一个空格。
例如,查找当前目录下的所有普通文件,并用ls命令输出:
find . -type f -exec ls -l {} /;
使用-exec
动作项处理匹配到的文件时,find
命令会将所有匹配到的文件一起传递给exec
执行。但有些系统对能够传递给exec
的命令长度有限制,这样在find
命令运行几分钟之后,就会出现溢出错误。错误信息通常是“参数列太长”或“参数列溢出”。这就是xargs
命令的用处所在,特别是与find
命令一起使用。
xargs
的使用格式是:
find PATH EXPR1 EXPR2 | xargs command
利用管道,把find
命令匹配到的文件名传递给xargs
命令,而xargs
命令每次只获取一部分文件而不是全部。这样它可以先处理最先获取的一部分文件,然后是下一批,并如此继续下去。
在有些系统中,使用-exec
动作项会为处理每一个匹配到的文件而发起一个相应的进程,并非将匹配到的文件全部作为参数一次执行;这样在有些情况下就会出现进程过多,系统性能下降的问题,因而效率不高;而使用xargs
命令则只有一个进程。另外,在使用xargs
命令时,究竟是一次获取所有的参数,还是分批取得参数,以及每一次获取参数的数目都会根据该命令的选项及系统内核中相应的可调参数来确定。
find
命令配合exec
和xargs
可以对所匹配到的文件执行几乎所有的命令。
扩展
查找包含指定内容的文件
[root@demo1 ~]# find ./ -type f | xargs grep "mysql is running";
./check_mysql.sh: echo "mysql is running"
[root@demo1 ~]# grep "mysql is running" check_mysql.sh
echo "mysql is running"