find & grep 總 結
前言
關於本文
總 結 了 find、grep常 規 用 法,正 則 表 達 式,find與 grep合 用 以 及 自 定 義 搜 索 函 數 等
什麽是find和grep
find 和 grep 是 linux中 最 常 用 的 兩 個 搜 索 函 數,本 文 將 會 介 紹 並 例 示 這 兩 個 函 數 的 用 法。
為什麽要用find和grep
對 其 的 熟 練 掌 握 可 以 明 顯 提 高 搜 索 效 率,尤 其 是 面 對 動 輒 幾 十 G 源 碼 時 。
關 於 參 數
find和grep有很多參數,可以用以下命令導出查看:
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:在當前目錄搜索除了
find ./ –not –name AndroidManifest.xml –a –name “*.xml”
例2:在當前目錄搜索名稱為values-zh-rCN或values的文件
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
用於指定查找文件的大小(單位b、k、M、G)
例1:在當前目錄查找所有小於10k的文件
find ./ –size -10k
例2:在當前目錄查找所有大於10M的文件
find ./ –size +10M
find搜索匹配進階及RE(正則表達式)
-name
-name 是將文件名去匹配而不是文件的輸出結果
* :代表任意字符(可以沒有字符)
? :代表任意單個字符
[] : 代表括號內的任意字符,[abc]可以匹配a\b\c某個字符
[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
查找匹配忽略大小寫,默認狀態下會匹配大小寫
參數 –l(L的小寫,不是大寫的i)
輸出結果只顯示文件名,不顯示行
參數 -s
不顯示不存在或無匹配文件的錯誤信息
參數 –w
可以精確匹配後面的單詞,而不是字符串匹配
參數 -v
逆反模式,即輸出不匹配的所有行
grep中RE的使用以及egrep
find中所說的REgrep這邊都可以使用,後面不再贅述,需要註意的是 + 、?要用\轉義
什麽是egrep
egrep是grep的進化版,改進了許多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”
find和grep的在搜索中的實際應用-進階
前面說了這麽多,其實都是在為這一小節做鋪墊,相信在實際應用中大家不會在源碼中直接去grep搜索,這樣會相當耗時,因此,我們需要掌握一些技巧以使搜索變得簡單而又高效:
find和grep結合使用
在實際應用中,find和grep配合使用將會非常方便而迅速,先看一個例子:
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目錄(編譯生成的目錄)
-type f :指定文件類型為普通文件
-name "*\.java" : 指定匹配的文件名為.java文件
-print0 | xargs -0 : 忽略搜索中可能出現的錯誤信息,並將搜索到的文件作為結果向後傳遞並繼續執行
grep --color –n :用grep在之前搜索到的文件中進行內容搜索,輸出行號並標識顏色
"$@" :表示在使用jgrep函數時輸入的參數,這裏即為grep搜索的內容(本人未搞清這裏為什麽寫$@而不是$*、$1)
這麽分析下來,這個函數的意思應該很容易就可以理解了,在這個基礎上,大家可以根據實際情況舉一反三,以寫出適合自己的函數或者命令。
find、grep中xargs 和 | 的使用
| :管道命令符,它會將前一個命令的標準輸出作為後一個命令的標準輸入,這裏不展開了,具體作用可以自行尋找資料學習。
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方式寫命令。
編寫搜索函數
學習find和grep的使用是為了使用方便和提高效率,如果每次搜索都和上面的例子那樣敲一堆命令,雖然提高了效率,方便和使用性上卻大打折扣,因此,我們需要將命令進行抽象,編寫搜索函數,就如同之前所講的系統自帶的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文件,實用性也很強。
find & grep 總 結