1. 程式人生 > >使用grep搜尋程式碼的幾個示例

使用grep搜尋程式碼的幾個示例

作為基於windows系統工作的攻城獅,每天必須用sourceinsight,這工具確實好用,關鍵詞和語法著色,上下文聯想,程式碼自動補全,但是也經常發現有些不太方便的地方。例如:操作前需要先建立工程,這也沒什麼,但是如果只想臨時在某個程式碼包裡查詢符號變數什麼的,也得需要先建立工程;對於程式碼量很大的專案,如Android,工程的建立和解析都很麻煩;還有就是對二進位制搜尋支援不好,對搜尋的匹配也很有限。

好吧,剛發現sourceinsight還支援正則表示式搜尋,這個功能什麼時候出現的?

搜尋程式碼,或者是查詢關鍵詞,除了sourceinsight,那就應該是grep了。 
可是度娘一下”grep“看看你能收到什麼?大多數都是”grep命令詳解”,“grep命令和引數的用法”,要不就是“grep和正則表示式”,然後點進去就是給你羅列一大堆“grep”的選項,要不就是羅列一大堆正則表示式語法,是羅列,羅列啊~我TM不想知道“grep”的一大堆選項,要你說,執行“man grep”詳細到十萬八千里去了,我也不想去研究正則表示式的各種語法,我只想知道如何解決我我遇到的實際問題。

好吧,搜尋了半天,也還是解決不了問題,最好老老實實“man grep”去找答案吧。

下面,從碼農讀程式碼的角度,總結下我最常用的grep方式,也歡迎大家交流下grep的一些高階用法。

讀程式碼時的查詢通常比較簡單,就是想知道某些符號在哪個檔案定義或在哪些地方被引用,都是一些明確的符號,很少需要模糊查詢,所以用到複雜正則表示式的機會很少。

免不了囉嗦一下,“grep”的常用的幾個選項:

-r,遞迴查詢

-n,搜尋結果顯示行號

-i,忽略大小寫

-v,反向匹配

-w,匹配整個單詞

-E,匹配擴充套件的正則表示式

這裡以u-boot-2016.09程式碼為例,進行memcpy查詢操作。

1. 遞迴查詢並顯示行號
這個是最基本的查找了。

[email protected]:u-boot-2016.09$ grep -rn memcpy
1
在當前目錄查詢可以使用:

不指定目錄:”grep -rn memcpy”
用”.“指定當前目錄:”grep -rn memcpy .”
其實這兩者查詢結果一樣,但在輸出格式上是有區別的,具體留給你去比較好了。

2. 查詢不區分大小寫
[email protected]:u-boot-2016.09$ grep -rni memcpy
1
選項”-i“或略大小寫,這樣除了匹配“memcpy”外,還可以匹配一些巨集定義如”MEMCPY“和”Memcpy“等,如:

...
include/malloc.h:351:#define HAVE_MEMCPY
include/malloc.h:353:#ifndef USE_MEMCPY
include/malloc.h:354:#ifdef HAVE_MEMCPY
include/malloc.h:355:#define USE_MEMCPY 1
include/malloc.h:357:#define USE_MEMCPY 0
include/malloc.h:361:#if (__STD_C || defined(HAVE_MEMCPY))
include/malloc.h:365:void* memcpy(void*, const void*, size_t);
...
board/freescale/t102xrdb/spl.c:63:  /* Memcpy existing GD at CONFIG_SPL_GD_ADDR */
board/freescale/t102xrdb/spl.c:64:  memcpy((void *)CONFIG_SPL_GD_ADDR, (void *)gd, sizeof(gd_t));
board/freescale/t208xqds/spl.c:68:  /* Memcpy existing GD at CONFIG_SPL_GD_ADDR */
...

3. 排除指定檔案的搜尋結果
搜尋結果的第一列會顯示搜尋結果位於哪個檔案中,所以可以通過對搜尋結果第一列的過濾來排除指定檔案。

例如:編譯時生成的*.o.cmd檔案中帶了很多包含memcpy.h的行,如:

out/rpi_3_32b/drivers/input/.input.o.cmd:295:    $(wildcard include/config/use/arch/memcpy.h)
1
可以在搜尋結果中用反向匹配”-v“排除*.o.cmd檔案的匹配:

[email protected]:u-boot-2016.09$ grep -rn memcpy | grep -v .o.cmd
1
如果想排除多個生成檔案中的匹配,包括”*.o.cmd,*.s.cmd,*.o,*.map“等,有兩種方式:

使用多個-v依次對上一次的結果進行反向匹配:
[email protected]:u-boot-2016.09$ grep -rn memcpy | grep -v .o.cmd | grep -v .s.cmd | grep -v .o | grep -v .map
1
使用-Ev一次進行多個反向匹配搜尋:
[email protected]:u-boot-2016.09$ grep -rn memcpy | grep -Ev '\.o\.cmd|\.s\.cmd|\.o|\.map'
1
由於這裡使用了正則表示式”-E“,所以需要用”\“將”.“字元進行轉義

另外,也可以使用”--exclude=GLOB“來指定排除某些格式的檔案,如不在“*.cmd”,“*.o”和“*.map”中搜索:

[email protected]:u-boot-2016.09$ grep -rn --exclude=*.cmd --exclude=*.o --exclude=*.map memcpy
1
跟“--exclude=GLOB”類似的用法有“--include=GLOB”,從指定的檔案中搜索,如只在“*.cmd”,“*.o”和“*.map”中搜索:

[email protected]:u-boot-2016.09$ grep -rn --include=*.cmd --include=*.o --include=*.map memcpy
1
“--include=GLOB”在不確定某些函式是否被編譯時特別有用。 
例如,不確定函式rpi_is_serial_active是否有被編譯,那就查詢“*.o”檔案是否存在這個函式符號:

[email protected]:u-boot-2016.09$ grep -rn --include=*.o rpi_is_serial_active
Binary file out/rpi_3_32b/board/raspberrypi/rpi/built-in.o matches
Binary file out/rpi_3_32b/board/raspberrypi/rpi/rpi.o matches

顯然,從結果看,這個函式是參與了編譯的,否則搜尋結果為空。

如果想知道函式rpi_is_serial_active最後有沒有被連結使用,查詢生成的u-boot*檔案就知道了:

[email protected]:u-boot-2016.09$ grep -rn --include=u-boot* rpi_is_serial_active
Binary file out/rpi_3_32b/u-boot matches
1
2
可見u-boot檔案中找到了這個函式符號。

4. 不在某些指定的目錄查詢memcpy
如果指定了u-boot編譯的輸出目錄,例如輸出到out,則可以直接忽略對out目錄的搜尋,如:

[email protected]:u-boot-2016.09$ grep -rn --exclude-dir=out memcpy
1
忽略多個目錄(“out”和“doc”):

[email protected]:u-boot-2016.09$ grep -rn --exclude-dir=out --exclude-dir=doc memcpy
1
5. 查詢精確匹配結果
通常的“memcpy”查詢結果中會有一些這樣的匹配:“MCD_memcpy”,“zmemcpy”,“memcpyl”,“memcpy_16”等,如果只想精確匹配整個單詞,則使用-w選項:

[email protected]:u-boot-2016.09$ grep -rnw memcpy .
1
6. 查詢作為單詞分界的結果
“作為單次分界“這個表述不太準確,例如,希望“memcpy”的查詢中,只匹配“MCD_memcpy”,“memcpy_16”,而不用匹配“zmemcpy”,“memcpyl”這樣的結果,也就是memcpy以一個完整單詞的形式出現。

一般這種查詢就需要結合正則表示式了,用正則表示式去匹配單詞邊界,例如:

[email protected]:u-boot-2016.09$ grep -rn -E "(\b|_)memcpy(\b|_)"
1
關於正則表示式“(\b|_)memcpy(\b|_)”

“\b“匹配單詞邊界
“_“匹配單個下滑下
所以上面的表示式可以匹配:memcpy,memcpy_xxx,xxx_memcpy和xxx_memcpy_xxx等模式。(可能匹配的還有函式memcpy_,_memcpy和_memcpy_)

7. 檢視查詢結果的上下文
想在結果中檢視匹配內容的前後幾行資訊,例如想看巨集定義“MEMCPY”匹配的前三行和後兩行:

[email protected]:u-boot-2016.09$ grep -rn -B 3 -A 2 MEMCPY
1
選項B/A: 
-B 指定顯示匹配前(Before)的行數 
-A 指定顯示匹配後(After)的行數

8. grep和find配合進行查詢
find針是對檔案級別的粗粒度查詢,而grep則對檔案內容的細粒度搜索。 
所以grep跟find命令配合,用grep在find的結果中進行搜尋,能發揮更大的作用,也更方便。

例如,我想查詢所有makefile類檔案中對CFLAGS的設定。 
makefile類常見檔案包括makefile,*.mk,*.inc等,而且檔名還可能是大寫的。

可以通過find命令先找出makefile類檔案,然後再從結果中搜索CFLAGS:

[email protected]:u-boot-2016.09$ find . -iname Makefile -o -iname *.inc -o -iname *.mk | xargs grep -rn CFLAGS
1
這裡由於涉及到find命令,所以整個查詢看起來有點複雜了,也可以只用grep的--include=GLOB選項來實現:

[email protected]:u-boot-2016.09$ grep -rn --include=Makefile --include=*.inc --include=*.mk CFLAGS .
1
比較上面的兩個搜尋結果,是一樣的,但是有一點要注意:

grep命令的--include=GLOB模式下,檔名是區分大小寫的,而且沒有方式指定忽略檔名大小寫
剛好這裡搜尋的Makefile只有首字母大寫的形式,而不存在小寫的makefile,所以這裡碰巧是結果一致而已,否則需要指定更多的--include=GLOB引數。

9. 一些練習
在linux目錄中查詢所有的*.h,並在這些檔案中查詢“SYSCALL_VECTOR”

[email protected]:linux-3.14-1.5$ find linux -name *.h | xargs grep "SYSCALL_VECTOR"
linux/arch/x86/include/asm/irq_vectors.h:#define IA32_SYSCALL_VECTOR            0x80
linux/arch/x86/include/asm/irq_vectors.h:# define SYSCALL_VECTOR                        0x80
linux/arch/m32r/include/asm/syscall.h:#define SYSCALL_VECTOR          "2"
linux/arch/m32r/include/asm/syscall.h:#define SYSCALL_VECTOR_ADDRESS  "0xa0"

在練習1的基礎上,打印出所有包含”SYSCALL_VECTOR”字串的檔名

[email protected]:linux-3.14-1.5$ find linux -name *.h | xargs grep -l "SYSCALL_VECTOR" 
linux/arch/x86/include/asm/irq_vectors.h
linux/arch/m32r/include/asm/syscall.h

grep的選項-l只打印匹配的檔名。

以上是我的一些grep用法,歡迎交流,共同提高讀程式碼的效率。
--------------------- 
作者:guyongqiangx 
來源:CSDN 
原文:https://blog.csdn.net/guyongqiangx/article/details/70161189 
版權宣告:本文為博主原創文章,轉載請附上博文連結!