前言

关于本文

findgrep 法,正 式,find grep

什么是findgrep

find grep linux 数,本 法。

为什么要用findgrep

率,尤 G

关 于 参 数

findgrep有很多参数,可以用以下命令导出查看:

man grep > grep.txt
man find > find.txt
 

参数可以帮助我们在使用时提高效率,实现特定需求以及避免错误,本文会介绍一些常用参数。

两者的区别,用find还是用grep

关于用find还是用grep,只要掌握一个原则就可以了:

凡是搜索文件名,就用find

凡是搜索文件内容,就要grep(当然为了提高效率可以结合find使用,后面会讲)

find基本用法

搜索文件名

例:在当前目录搜索名称为DialactsActivity.java的文件

find ./ –name DialactsActivity.java
 

参数 –a  –o  –not

分别为与、或、非的意思,相当于find中的逻辑运算

1:在当前目录搜索除了AndroidManifest.xml文件以外其他的xml文件

 

find ./ –not –name AndroidManifest.xml –a –name “*.xml”
 

 

2:在当前目录搜索名称为values-zh-rCNvalues的文件

find ./ -name values-zh-rCN -o -name values
 

参数 –prune

该参数的作用是忽略某个文件,即不在这个文件夹里搜索文件以提高效率,该参数常和-o一起使用

1:在当前目录且不在res文件中搜索名为values-zh*的文件并打印

find ./ -name res -prune -o -name values-zh* -print
 

Note最后的-print,如果不加-print结果中除了打印不在res文件中的values-zh*文件之外还会打印find . -name res这个搜索出来的结果

tips*为通配符

参数 –exec

find命令后加入该参数可以对搜索结果进行处理

例:在当前目录搜索所有java文件并将其删除

find ./ –name “*.java” –exec rm –rf {} ;
 

Note{} 表示前面搜索的结果, “; ”表示命令结束, ”用于转义且前面必须要有空格

参数 –type

该参数用于指定查找文件的类型

1:在当前目录查找文件名包含res的文件夹

find ./ –name “*res*” –type d
 

-type参数后跟指定的文件类型,d为文件夹,f为普通文件

参数 –size

用于指定查找文件的大小(单位bkMG

1在当前目录查找所有小于10k的文件

find ./ –size -10k

2:在当前目录查找所有大于10M的文件

find ./ –size +10M
 

find搜索匹配进阶及RE(正则表达式)

-name

-name      是将文件名去匹配而不是文件的输出结果
*        : 代表任意字符(可以没有字符)
?       :  代表任意单个字符
[]       : 代表括号内的任意字符,[abc]可以匹配ac某个字符
[a-z]    : 可以匹配a-z的某个字母
[A-Z]    : 可以匹配A-Z的某个字符
[0-9]    : 可以匹配0-9的某个数字
如果方括号内加入“ ^ ”,则表示不去匹配里面的字符
[^a-z]   : 表示不匹配a-z的某个字符。
例1      : 在当前目录中搜素不以a、b、c开头的所有文件
find ./ –name “[^abc]*”
例2:在当前目录中搜索以大写字母或数字开头的所有文件
find ./ -name “[A-Z0-9]*”
 

-regex

-regex是将文件的输出结果进行匹配而不是文件名

比如,当前目录中有a文件夹,a中有b文件夹,b中有c文件

c文件的文件名为c,输出结果为./a/b/c,前者用于-name匹配,后者用于-regex匹配

-regex相对于-name的优势是可以使用正规的RE

例:搜索所有输出结果包含res的文件(哪怕文件名不包含res,只要该文件在res文件夹中也都可以被搜索到)

find . –regex “.*res.*”

Note这里匹配所有字符是 .* 而不是 *

简单的RE符号及用法

[]     : 与之前find中描述相同,在此不予赘述
.        : 表示任意单个字符
?  : 表示前面的字符出现一次或零次
+       : 表示前面的字符至少出现一次
*       : 表示前面的字符出现零次或多次
()       :将表示的字符括起来后面跟量词
例:在当前目录搜索输出结果至少出现一次res的所有文件
         find ./ -regex “.*(res)+.*”
Note     :()要用转义,该例将会打印出所有包含res的目录及其中的文件
|       : 逻辑或,可以搜索两个条件
例:在当前目录搜索所有文件名末尾为res或res_ext的文件
         find ./ -regex “.*res|.*res_ext”
 

grep基本用法

先用两个例子看grep最简单的用法

1:在Android.mk文件中搜索包含res的行

grep “res” Android.mk
 

2:在当前目录的所有文件中搜索包含res的行

grep “res” ./*
 

参数 –r

表示在当前目录和子目录中循环搜索(加入该参数可以不用指定文件,意为已经指定了当前目录及子目录中的所有文件)

例:在当前目录及子目录中的所有文件中搜索包含res的行

grep –r “res”
 

参数 –n

输出的结果打印行号

例:在当前目录及子目录中的所有文件中搜索包含res的行并打印行号

grep –nr “res”
 

参数 –i

查找匹配忽略大小写,默认状态下会匹配大小写

参数 –lL的小写,不是大写的i

输出结果只显示文件名,不显示行

参数 -s

不显示不存在或无匹配文件的错误信息

参数 –w

可以精确匹配后面的单词,而不是字符串匹配

参数 -v

逆反模式,即输出不匹配的所有行

grepRE的使用以及egrep

find中所说的REgrep这边都可以使用,后面不再赘述,需要注意的是 + 、?要用转义

什么是egrep

egrepgrep的进化版,改进了许多grep中不方便之处如下:

egrep使用RE符号 + | (或) , {} 时不用转义,如果要用其本身则需要/转义

所以,如果需要用到RE的话,尽量选择egrep

简单的RE符号及用法

< , >       :分别表示单词的开始和结束,将单词放入其中可以精确匹配(类似于-w)
         例:搜索精确匹配Dialer单词的行(形如DialerActivity则不会匹配)
 egrep –nr “<Dialer>”
 Note: >表示的是单词的结束,单词可能是下一个单词,这里写的时候需要注意
 ^ , $           : 分别表示行的开始和结束(^ 用在 [ ] 内表示不匹配其中的字符,注意区别)
{n}             : 表示前面的字符匹配n次
{n,m}           : 表示前面的字符匹配n-m次
{,m}            : 表示前面的字符至多匹配的m次
{n,}            : 表示前面的字符至少匹配n次
[ ]                : 方括号使用和find一样,也可以使用国际模式,但感觉不如直接写形如[0-9a-zA-Z]易于理解,因此不予展开
[[:space:]]     :表示空格或tab
例1:当前目录搜索包含access your和Phone permission的行(如果中间的字符串看不清或者不方便写或者可能是转义符如换行符等,可以用.*代替)
egrep –nr “access your.*Phone permission”
 

findgrep的在搜索中的实际应用-进阶

前面说了这么多,其实都是在为这一小节做铺垫,相信在实际应用中大家不会在源码中直接去grep搜索,这样会相当耗时,因此,我们需要掌握一些技巧以使搜索变得简单而又高效:

findgrep结合使用

在实际应用中,findgrep配合使用将会非常方便而迅速,先看一个例子:

function jgrep()
{
    find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*.java" -print0 | xargs -0 grep --color -n "$@"
}
 

这个是位于Android源码build/envsetup.sh中的jgrep函数,用于搜索java文件内容,是经常使用的一个函数,是find结合grep的典型案例。如果已经确定了搜索内容在java文件中,那么相比于直接用grep进行全局搜索,这种先搜索所有的java文件,再在其中搜索内容的方式可以显著提高效率。

我们先来解析一下这个函数:
find .                                :在当前目录搜索
-o                                     : 或,并列多个条件
-name .repo –prune        : 忽略.repo目录(git库相关)
-name .git –prune           : 忽略.git目录(git库相关)
-name out –prune           : 忽略out目录(编译生成的目录)ds
-type f                             :指定文件类型为普通文件
-name "*.java"              : 指定匹配的文件名为.java文件
-print0 | xargs -0             : 忽略搜索中可能出现的错误信息,并将搜索到的文件作为结果向后传递并继续执行
grep --color –n               :用grep在之前搜索到的文件中进行内容搜索,输出行号并标识颜色
"$@"                              :表示在使用jgrep函数时输入的参数,这里即为grep搜索的内容(本人未搞清这里为什么写$@而不是$*$1)
这么分析下来,这个函数的意思应该很容易就可以理解了,在这个基础上,大家可以根据实际情况举一反三,以写出适合自己的函数或者命令。
 

findgrepxargs | 的使用

|       :管道命令符,它会将前一个命令的标准输出作为后一个命令的标准输入,这里不展开了,具体作用可以自行寻找资料学习。
xargs: 如果仅使用 | ,那么前面的结果会作为输入直接传递到后面的命令中,而使用xargs,就可以使前面的结果作为参数传递到后面的命令中,而这个特性对于find和grep而言十分重要。下面举几个例子来说明find中xargs的用法:
例1:在当前目录中搜索所有AndroidManifest.xml文件并在其中搜索DialtactsActivity
find . –name AndroidManifest.xml | xargs grep –n –color “DialtactsActivity”
说明:该例是xargs最基本的用法,如果将xargs去掉,那么grep搜索的内容是find输出的结果内容而非结果文件
例2:在当前目录搜索所有的values-zh-rCN文件目录并在其中搜索所有的strings.xml文件(即所有中文字符串存放位置),然后在搜索到的strings.xml文件中搜索“通话”字符串
find . –type d –name “values-zh-rCN” | xargs –i find {} –name “strings.xml” | xargs grep –n –-color 通话
tips:该例中xargs后使用了-i参数,该参数的作用是可以将后面命令中的 {} 符号视为前面find搜索的结果文件。本例中连续使用了两次xargs进行结果的传递。
例3:在当前目录中的所有mk文件中搜索ro.build.type
find . –type f –name “*.mk” –print0 | xargs -0 grep –n –color “ro.build.type”
tips:本例中和之前提到的jgrep函数都是用了 –print0 | xargs -0进行结果传递而非单纯使用xargs,这样做的好处是如果find搜索会忽略可能出现的错误,使最终输出的结果更清晰,因此在使用xargs时建议按照–print0 | xargs -0方式写命令。
 

编写搜索函数

学习findgrep的使用是为了使用方便和提高效率,如果每次搜索都和上面的例子那样敲一堆命令,虽然提高了效率,方便和使用性上却大打折扣,因此,我们需要将命令进行抽象,编写搜索函数,就如同之前所讲的系统自带的jgrep函数一样,做到真正的实用而又高效。

为了更加直观,举一个例子来说明如何编写搜索函数,这里需要用到一些简单的编写shell脚本基础知识:

#文件内容搜索函数sep
#参数1 必选 搜索内容
#参数2 可选 前缀-t 内容所在的文件类型(即文件后缀名,如java),缺省为所有文件类型
#参数3 可选 前缀-f 指定搜索的目录 缺省为当前目录及所有子目录
#用例 sep "new ITelecomService.Stub"  -t  java xml  –f  packages/ frameworks/
#用例解析 在packages、frameworks目录中的所有java、xml文件中搜索"new ITelecomService.Stub"
function sep()
{
#文件内容=第一个参数
se_content=$1
#文件类型和搜索目录暂时=空
se_fileType=""
se_folder=""
#shift的作用是将第一个参数移除,即当前函数输入的第二个参数变成第一个参数,第三个变成第二个,以此类推
shift
#判断当前第一个参数是否为-t,即文件类型是否指定,如果指定就取出文件类型放入se_fileType变量中
if [ "$1" = "-t" ];then
    #如果是-t就将这个参数移除
    shift
    while ( [ "$1" != "-f" ] && [ -n "$1" ] )
    do
        se_fileType="$se_fileType $1"
        shift
    done
fi
#判断当前第一个参数是否为-f,即搜索目录是否指定,如果指定就取出搜索目录放入se_folder变量中
if [ "$1" = "-f" ];then
    #如果是-f就将这个参数移除
    shift
    while [ -n "$1" ]
    do
        se_folder="$se_folder $1"
        shift
    done
fi
#判断文件类型是否为空,不为空则建立循环分别搜索指定的文件类型
if [ -z $se_fileType ];then
    #这里如果搜索目录为空find会自动搜索当前目录及子目录,因此不用再做判断
         #这里用到了egrep而不是grep,方便输入搜索内容时直接使用正则表达式
    find $se_folder -type f -print0 | xargs -0 egrep -n --color "$se_content"
else
    for ft in $se_fileType
    do
        find $se_folder -type f -name "*.$ft" -print0 | xargs -0 egrep -n --color "$se_content"
    done
fi
}
这个函数整体而言比较简单,加上其中的注释,想必大家可以很容易理解,在这个基础之上我们还可以添加其他参数,比如是否精确匹配等,这里不再具体说明了。
关于函数如何使用:写到.sh文件中再用source命令导入即可在命令行直接使用,这是linux中最基本的操作,不太明白的同学可自行百度。
tips:也可以仿照前面说的jgrep函数编写简单的函数如下:

function mkgrep()
{
    find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*.mk" -print0 | xargs -0 grep --color -n "$@"
}
只是把java改成mk就可以直接搜索所有mk文件,实用性也很强。