find&正则表达式
阅读原文时间:2023年07月08日阅读:2

标准的正则表示式格式

常用元字符

代码

说明

.

匹配除换行符以外的任意字符

\w

匹配字母或数字或下划线

\s

匹配任意的空白符

\d

匹配数字

\b

匹配单词的开始或结束

^

匹配字符串的开始

$

匹配字符串的结束

常用限定符

代码/语法

说明

*

重复零次或更多次

+

重复一次或更多次

?

重复零次或一次

{n}

重复n次

{n,}

重复n次或更多次

{n,m}

重复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 ] {} \;

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

其他使用方法

模糊匹配路径

root@王初一:~# find . -path '*20190326/access.log'
./logs/20190326/access.log

-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 -o -path ) -prune -o -path

注意:如果同时使用-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"