本文原创发布于微信公众号“洛奇看世界”
网上搜索 grep 的结果基本上都是一些跟运维相关的操作,并不适用于阅读代码。
当你不想使用复杂的代码阅读工具时,快速查找代码,使用 grep 非常方便。
这里跟大家分享一下我的 grep 笔记。
这个 grep 笔记一开始比较简单,但随着各种操作的需要,出现了新的知识点,然后不断迭代,几年后差不多就是现在这个样子。
懒得整理了,除了在第三部分临时增加了两个例子之外,真的就是我自己的 grep 笔记。
本文主要包含 3 个部分:
- 第一部分总结了 grep 命令的一些注意事项;
- 第二部分汇总了 grep 命令的多种选项用法;
- 第三部分总结我用 grep 看代码用得最多的方法;
除了用于阅读代码之外,平时管理服务器也经常用 grep 命令,所以对各个命令常用的场景做了标记,包括看代码常用和运维常用两种。
1. grep
的命令格式
1
2
3# grep [选项] 搜索模式 [文件和目录列表] grep [OPTION]... PATTERN [FILE]...
1.1 注意事项
- 引号问题
建议: 三剑客 grep
,sed
和 awk
的模式字符串都使用单引号 ‘’ 包含
搜索的模式 PATTERN
用单引号 ''
或双引号 ""
包含起来,避免被解释为文件。
例如搜索 “bash shell” 字符串,如果使用:
1
2$ grep bash shell /etc/test
会被理解为从shell
和/etc/test
两个文件中查找bash
字符串,所以应该使用引号包含:
1
2
3$ grep 'bash shell' /etc/test $ grep "bash shell" /etc/test
这个引号最容易出问题还是在
find
命令中。
- 搜索字符串中带有连字符
-
问题
如果搜索的 PATTERN
中带有连字符 -
,使用反斜线 将其转义,如: 从
grep
命令的帮助信息中查找-c
选项的信息:
1
2
3
4$ grep --help | grep '-c' Usage: grep [OPTION]... PATTERN [FILE]... Try 'grep --help' for more information.
这个命令中,前半部分 grep --help
执行是正确的,可以用其它命令 tar --help | grep '-c'
测试。
但是后半部分 grep '-c'
操作出错了,因为这里将 -c
字符串当做选项使用了,使用反斜线转义-
字符解决(下面每一行的结果中都包含了 -c
字符串):
1
2
3
4
5
6
7
8
9
10
11$ grep --help | grep '-c' -i, --ignore-case ignore case distinctions -m, --max-count=NUM stop after NUM matches -c, --count print only a count of matching lines per FILE -B, --before-context=NUM print NUM lines of leading context -A, --after-context=NUM print NUM lines of trailing context -C, --context=NUM print NUM lines of output context -NUM same as --context=NUM --color[=WHEN], --colour[=WHEN] use markers to highlight the matching strings;
1.2 输出值和返回值
什么是函数的输出值和返回值,二者有什么区别?
- 输出值
grep
操作的输出内容作为输出值, 对于一般函数来讲,就是函数内部的 echo
输出。
所以输出值大多数时候是终端查找时用于显示, shell程序中更关心返回值, 用于判断是否存在匹配模式。
如:
1
2
3
4
5
6
7
8
9$ grep 'root' /etc/passwd root:x:0:0:root:/root:/bin/bash $ item=$(grep 'root' /etc/passwd) $ echo $? 0 $ echo $item root:x:0:0:root:/root:/bin/bash
这里 grep 'root' /etc/passwd
的输出内容为 root:x:0:0:root:/root:/bin/bash
。
所以变量 $item
获取的输出值为 root:x:0:0:root:/root:/bin/bash
- 返回值
返回值一般都是给shell程序使用, 如果指向在终端使用grep查找, 则不用关心其返回值)
grep
操作会返回其状态,对于一般函数来讲,就是函数内部的 return
和 exit
等。
返回值通过变量 $?
获取。
grep
的man手册中是这样定义返回值的:
1
2
3
4
5EXIT STATUS The exit status is 0 if selected lines are found, and 1 if not found. If an error occurred the exit status is 2. (Note: POSIX error handling code should check for '2' or greater.)
退出状态如下:
- 找到匹配内容返回0,
- 没有查找到匹配内容返回1,
- 失败返回2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23# 1. 查找root成功,返回0 $ grep -q "root" /etc/passwd; echo $? 0 # 2. 没有找到匹配内容,返回1 $ grep -q "rocky" /etc/passwd; echo $? 1 # 3. 查找失败,返回2 $ grep -q "rocky" /etc/password; echo $? grep: /etc/password: No such file or directory 2 $ grep -q -s "rocky" /etc/password; echo $? 2 # 选项: # "-q"选项,抑制正常输出(安静查找) # "-s"选项, 抑制错误消息 $ grep --help | grep -Ew "-[s|q]" -s, --no-messages suppress error messages -q, --quiet, --silent suppress all normal output if any error occurs and -q is not given, the exit status is 2.
通常用于 shell 脚本中不获取输出,仅获取其操作结果返回值的情况,如:
1
2
3
4
5# 在 /etc/nsswitch.conf 文件中搜索 "automount" 开始的行 if ! grep -q "^automount" /etc/nsswitch.conf; then sed -i "s/^netgroup:.*/&nautomount: files nis/" /etc/nsswitch.conf fi
这里如果在 /etc/nsswitch.conf
中搜索到 ^automount
的匹配内容,则 grep
操作返回 0。条件语句 ! grep -q "/etc/auto.direct" /etc/nsswitch.conf
的结果为真,条件成立,执行随后的语句。
另一个例子:
1
2
3
4
5
6if [ "$CONF_NFS" != "0" ]; then if ! grep -q "^/local/git" /etc/exports; then echo "/local/git $STBSITE-*(ro,sync,root_squash,mp=/local,no_subtree_check)" >> /etc/exports fi fi
2. grep
的常用选项
2.1 选项 -n
,显示行号
看代码常用
1
2
3
4
5# 在 /etc/passwd, /etc/group 文件中搜索 root 字符串 $ grep -n "root" /etc/{passwd,group} /etc/passwd:1:root:x:0:0:root:/root:/bin/bash /etc/group:1:root:x:0:
2.2 选项 -r/R
,递归查找
看代码常用
如果指定了目录,默认仅在指定目录下查找,使用 -r/R
后会递归在所有子目录下查找。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47# # 'tree . -l'选项列举符号链接文件和目录 # $ tree . -l . ├── df.txt ├── gnu-utils -> ../gnu-utils │ ├── info-find.txt │ ├── info-man.txt │ ├── info-sed.txt │ ├── info-xargs.txt │ ├── man-find.txt │ ├── man-grep.txt │ ├── man-help.txt │ ├── man-man.txt │ ├── man-sed.txt │ └── man-xargs.txt ├── info-grep.txt ├── man-grep.txt └── man-tree.txt 1 directory, 14 files $ # 这里当前目录的"gnu-utils"连接到"../gnu-utils" # # '-r'递归搜索,但不跟踪符号链接文件和目录 # $ grep -rl "grep" . ./info-grep.txt ./man-grep.txt $ # # '-R'递归搜索,跟踪符号链接文件和目录 # 'gnu-utils'为符号链接目录 # $ grep -Rl "grep" . ./info-grep.txt ./man-grep.txt ./gnu-utils/info-sed.txt ./gnu-utils/info-xargs.txt ./gnu-utils/man-grep.txt ./gnu-utils/man-sed.txt ./gnu-utils/man-find.txt
-r
和 -R
的区别:
-r
,递归查找-R
,递归查找,并查找符号链接(symlinks
)文件和目录。
3. 选项 -i
,忽略大小写
看代码常用
- 默认搜索会区分大小写
1
2
3$ grep --help | grep -w "-c" -c, --count print only a count of matching lines per FILE
- 带
-i
不区分大小写
1
2
3
4$ grep --help | grep -w -i "-c" -c, --count print only a count of matching lines per FILE -C, --context=NUM print NUM lines of output context
4. 选项 -w
,匹配完整单词
看代码常用
- 默认会搜索到很多包含
"-c"
内容的字符串
1
2
3
4
5
6
7
8
9
10
11$ grep --help | grep "-c" -i, --ignore-case ignore case distinctions -m, --max-count=NUM stop after NUM matches -c, --count print only a count of matching lines per FILE -B, --before-context=NUM print NUM lines of leading context -A, --after-context=NUM print NUM lines of trailing context -C, --context=NUM print NUM lines of output context -NUM same as --context=NUM --color[=WHEN], --colour[=WHEN] use markers to highlight the matching strings;
- 带
-w
选项精确匹配"-c"
字符串:
1
2
3$ grep --help | grep -w "-c" -c, --count print only a count of matching lines per FILE
5. 选项 -A/B/C
,显示上下文
看代码常用
-A
, after,在搜索结果中显示匹配点后面的若干行-B
, before,在搜索结果中显示匹配点前面的若干行-C
, context,在搜索结果中显示匹配点前面和后面的若干行
在查看代码时,除了搜索结果,还经常想看下匹配结果的上下文时,特别好用。
6. 选项 -c
,显示匹配数量
运维常用
- 默认显示匹配的所有内容
1
2
3
4
5
6
7
8
9# 查找ip地址 $ grep -E "([0-9]{1,3}.){3}[0-9]{1,3}" /etc/network/interfaces address 10.148.7.17 netmask 255.255.254.0 network 10.148.6.0 broadcast 10.148.7.255 gateway 10.148.6.1 dns-nameservers 192.19.189.30 192.19.189.20 192.19.189.10
- 带
-c
选项仅显示匹配的数量
1
2
3
4# 查找ip地址 $ grep -E -c "([0-9]{1,3}.){3}[0-9]{1,3}" /etc/network/interfaces 6
- 代码片段
1
2
3
4
5# 如果搜索的匹配数量不为0,则执行后续操作 if [ 0$(grep -c "^rsnapshot" /etc/auto.projects) -eq 0 ]; then echo "rsnapshot -fstype=nfs,ro,nobind $BAKHOST:$BAKPATH/rsnapshot" >> /etc/auto.projects fi
7. 选项 --exclude/--exclude-dir
,排除搜索的文件和路径
看代码常用
- 默认搜索时会查找所有文件和目录
1
2
3
4
5
6
7
8
9
10
11
12bolt-android.git$ grep -rnwl sec_read_otp_bit . ./security/7445e0/bsl-lib.a ./security/verify_ssbl.c ... ./security/bfw_load.c ./ssbl/ui/ui_secboot.c ./objs/7260b0/security/bsp_utils.i ... ./fsbl/fsbl-hacks.c ./fsbl/fsbl-main.c ./fsbl/fsbl-sec.c
- 带
--exclude=GLOB
选项,指定不进行搜索的文件类型,GLOB为通配符
1
2
3
4
5
6
7
8
9
10# 使用多个 --exclude 进行排除,命令太长了 $ grep -rnwl sec_read_otp_bit . --exclude=*.s --exclude=*.i --exclude=*.o --exclude=*.a # 使用单个 --exclude 进行排除 # 例1. 查找包含字符串 sec_read_otp_bit 的文件(排除编译生成文件和库文件) $ grep -rnwl sec_read_otp_bit . --exclude=*.{s,i,o,a} # 例2. 查找字符串FIRST(带有FIRST的宏,但不在.h和.c文件中搜索) $ grep -rn FIRST . --exclude={*.c,*.h}
- 带
--exclude-dir=DIR
选项,指定不进行搜索的目录
1
2
3
4
5
6# 使用多个 --exclude-dir 进行排除 $ grep -rnwl sec_read_otp_bit . --exclude-dir=objs --exclude-dir=security # 使用单个 --exclude-dir 进行排除 $ grep -rnwl sec_read_otp_bit . --exclude-dir={objs,security}
8. 选项 --include
, 指定搜索的文件
看代码常用
-
默认会在所有文件中搜索
-
带
--include=GLOB
选项,在指定的文件中搜索
1
2
3
4
5
6# 使用多个--include进行指定 $ grep -rnwl sec_read_otp_bit . --include=*.c --include=*.s # 使用单个--include和通配符进行指定 $ grep -rnwl sec_read_otp_bit . --include=*.{c,s}
千万不要问:
- 排除搜索时有
--exclude=GLOB
和--exclude-dir=DIR
,- 指定搜索时有
--include=GLOB
,- 为什么没有
--include-dir=DIR
?因为直接在
"grep [OPTION] PATTERN DIR ..."
时直接指定一个或多个DIR就可以了~~
9. 选项 -I
,不搜索二进制文件
看代码有时用
默认会搜索所有文件,将二进制文件也当做text进行搜索。
如果指定-I
或--binary-files=without-match
选项,则二进制文件即使包含相应字符串,也当做不包含该内容处理。
1
2
3
4
5
6
7
8
9$ grep -rnwlI sec_read_otp_bit . ./security/verify_ssbl.c ./security/boot_defines.h ... ./objs/7260b0/security/bsp_utils.i ./objs/7260b0/security/verify_ssbl.i ./objs/7260b0/security/scramble.s ...
这里搜索字符串sec_read_otp_bit
时,不再从*.{a,o,bin}
文件中搜索
10. 选项 -v
, 反向查找
运维常用
- 默认显示匹配内容的行
1
2
3
4
5# 搜索"#"开始的行(注释行) $ grep "^#" /etc/passwd # adminbse:x:200:200:Admin Bse,,,:/home/adminbse:/bin/bash # 2016-07-29 adminbse jjwong - Change adminbse to adm(4) group
- 带
-v
选项显示不匹配内容的行
1
2
3
4
5
6
7# 搜索不以"#"字符开始的行 $ grep -v "^#" /etc/passwd root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin ...
11. 选项 -q
,静默查找
运维常用
- 默认显示输出结果
1
2
3
4
5
6
7
8
9
10$ grep -rs "^root" /etc /etc/lxctl/lxctl.yaml:root: /etc/services:rootd 1094/tcp /etc/services:rootd 1094/udp /etc/group:root:x:0: /etc/postfix/virtual:root root@localhost /etc/passwd:root:x:0:0:root:/root:/bin/bash /etc/newt/palette.ubuntu:root=,magenta /etc/newt/palette.ubuntu:roottext=,magenta
- 带
-q
不显示搜索匹配结果
1
2
3
4$ grep -rq "^root" /etc $ echo $? 0
通常用于shell脚本中不获取输出,仅获取其操作结果返回值的情况,如:
1
2
3
4
5# 在/etc/fstab文件中搜索"/etc/auto.direct" if ! grep -q "/etc/auto.direct" /etc/fstab; then echo -e "n# $TODAY $AUTHOR - Use /etc/auto.direct for NFS mounts" >> /etc/fstab fi
这里如果在/etc/fstab
中搜索到/etc/auto.direct
的匹配内容,则grep
操作返回0。条件语句 ! grep -q "/etc/auto.direct" /etc/fstab
的结果为真,条件成立,执行随后的语句。
另一个例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16if ! grep -q "guyongqiangx" /etc/yp.conf; then NISLIST="nis2.she.guyongqiangx.com nis1.she.guyongqiangx.com" echo -e "n# $TODAY $AUTHOR - NIS servers" >> /etc/yp.conf echo -e "n# $TODAY $AUTHOR - NIS servers" >> /etc/hosts for NISN in $NISLIST; do echo "domain $(cat /etc/defaultdomain) server $NISN" >> /etc/yp.conf NISIP=$(host $NISN | grep -o "has address .*" | cut -d' ' -f3) if [ ! -z $NISIP ]; then echo -e "$NISIPt$NISN" >> /etc/hosts else echo -e "# 0.1.2.3t$NISN" >> /etc/hosts fi done fi fi
使用 grep -c
进行操作的例子(这里获取的不再是 grep
执行的返回值,而是获取 grep
操作获取的内容):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18# 在/etc/auto.projects中搜索"^rsnapshot" $ grep "^rsnapshot" /etc/auto.projects rsnapshot -fstype=nfs,ro,nobind stbszx-bak-1:/local/backups/rsnapshot # 在/etc/auto.projects中搜索"^rsnapshot"匹配的数量 $ grep -c "^rsnapshot" /etc/auto.projects 1 # 0与搜索结果合并,构成数字01 # $(grep -c "^rsnapshot" /etc/auto.projects)获取操作返回的内容 $ echo 0$(grep -c "^rsnapshot" /etc/auto.projects) 01 # 如果搜索的匹配数量不为0,则执行后续操作 if [ 0$(grep -c "^rsnapshot" /etc/auto.projects) -eq 0 ]; then echo "rsnapshot -fstype=nfs,ro,nobind $BAKHOST:$BAKPATH/rsnapshot" >> /etc/auto.projects fi
12. 选项 -s
,抑制错误信息
运维常用
- 默认显示错误信息
1
2
3
4
5
6
7
8
9$ grep -r rocky /etc/ grep: /etc/gshadow: Permission denied /etc/subgid:rocky:165536:65536 grep: /etc/security/opasswd: Permission denied grep: /etc/default/cacerts: Permission denied ... grep: /etc/group-: Permission denied grep: /etc/polkit-1/localauthority: Permission denied
- 带
-s
抑制错误信息
1
2
3
4
5
6
7$ grep -rs rocky /etc/ /etc/subgid:rocky:165536:65536 /etc/subuid:rocky:165536:65536 /etc/passwd:rocky:x:1001:1001:rocky:/home/rocky:/bin/bash /etc/group:sudo:x:27:ygu,rocky /etc/group:rocky:x:1001:rocky
相当于将错误输出重定向到/dev/null
,即2>/dev/null
1
2
3
4
5
6
7$ grep -r rocky /etc/ 2>/dev/null /etc/subgid:rocky:165536:65536 /etc/subuid:rocky:165536:65536 /etc/passwd:rocky:x:1001:1001:rocky:/home/rocky:/bin/bash /etc/group:sudo:x:27:ygu,rocky /etc/group:rocky:x:1001:rocky
13. 选项 -h
,搜索输出中不显示文件名
运维常用
- 单个文件搜索时,搜索结果中不显示文件名,只有在多文件搜索是才会存在文件名:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20# 单个文件搜索 $ grep -s "^root" /etc/passwd root:x:0:0:root:/root:/bin/bash # 多个文件搜索 $ grep -s "^root" /etc/passwd /etc/group /etc/passwd:root:x:0:0:root:/root:/bin/bash /etc/group:root:x:0: # 目录搜索 $ grep -rs "^root" /etc /etc/lxctl/lxctl.yaml:root: /etc/services:rootd 1094/tcp /etc/services:rootd 1094/udp /etc/group:root:x:0: /etc/postfix/virtual:root root@localhost /etc/passwd:root:x:0:0:root:/root:/bin/bash /etc/newt/palette.ubuntu:root=,magenta /etc/newt/palette.ubuntu:roottext=,magenta
- 默认搜索结果中显示文件名
1
2
3
4
5
6
7
8
9
10$ grep -rs "^root" /etc /etc/lxctl/lxctl.yaml:root: /etc/services:rootd 1094/tcp /etc/services:rootd 1094/udp /etc/group:root:x:0: /etc/postfix/virtual:root root@localhost /etc/passwd:root:x:0:0:root:/root:/bin/bash /etc/newt/palette.ubuntu:root=,magenta /etc/newt/palette.ubuntu:roottext=,magenta
- 带
-h
选项的结果中不显示文件名
1
2
3
4
5
6
7
8
9
10$ grep -rsh "^root" /etc root: rootd 1094/tcp rootd 1094/udp root:x:0: root root@localhost root:x:0:0:root:/root:/bin/bash root=,magenta roottext=,magenta
14. 选项 -o
,只显示匹配内容
运维常用
- 默认显示所有匹配的行
1
2
3
4
5
6
7
8
9# 查找ip地址 $ grep -E "([0-9]{1,3}.){3}[0-9]{1,3}" /etc/network/interfaces address 10.148.7.17 netmask 255.255.254.0 network 10.148.6.0 broadcast 10.148.7.255 gateway 10.148.6.1 dns-nameservers 192.19.189.30 192.19.189.20 192.19.189.10
- 带
-o
选项仅显示匹配的内容
1
2
3
4
5
6
7
8
9
10
11# 查找ip地址 $ grep -E -o "([0-9]{1,3}.){3}[0-9]{1,3}" /etc/network/interfaces 10.148.7.17 255.255.254.0 10.148.6.0 10.148.7.255 10.148.6.1 192.19.189.30 192.19.189.20 192.19.189.10
示例2:
Android 的 manifest 文件中有很多 project,每个 project 都有一个 groups 选项,显示所有的 gropus 选项:
1
2
3
4
5
6
7... <project groups="device,yukawa,pdk" name="device/amlogic/yukawa" revision="e6bdb83eb57cd9fa46d827950b4d52ffe8e101d3"/> ... <project groups="pdk-cw-fs,pdk" name="device/common" revision="9d2a0d0c3aab8b698669bad9810bc52ac7f76dfb"/> <project groups="pdk" name="device/generic/arm64" revision="b2aea8576e3928e9add8b616740e56c1c5a811d2"/> ...
查找所 有groups 匹配的字符串(这里包含了 “groups=” 字符串本身):
1
2
3
4
5
6
7
8$ grep -oE "groups="[^"]*?"" .repo/manifest.xml | sort -u | uniq groups="adt-infra,cts,developers,motodev,pdk,tools,tradefed" groups="adt-infra,notdefault,pdk-fs" groups="apps_nfc,pdk-fs" groups="apps_se,pdk-fs" groups="apps_stk,pdk-fs" ...
这里只显示匹配内容有什么用呢?这里可以获取匹配的内容做进一步处理,例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19# 1. 通过grep操作提取子域名sub-domain # 'grep -Po "(?<=^searchs).+" /etc/resolv.conf', '-P'指定使用perl格式的正则表达式 SUBDOMAINS="$(grep -Po "(?<=^searchs).+" /etc/resolv.conf) guyongqiangx.net guyongqiangx.com $(hostname -d)" [ -n "$SUBDOMAINS" ] && for SUBDN in $SUBDOMAINS; do sed -i -r "/^s+dns-search.*$SUBDN/! s/^s+dns-searchs+/&$SUBDN /" /etc/network/interfaces if [ -d /etc/netplan ]; then sed -i -r "/^s+search:.*$SUBDN/! s/^s+search:.*[s*/&$SUBDN, /" /etc/netplan/7[0-9]*.yaml netplan --debug generate fi done # 2. 通过grep操作提取uid=1000的用户名,然后将其替换为adminbse # 'grep -Po "^[[:alnum:]-_]+(?=:x:1000:)" /etc/passwd', '-P'指定使用perl格式的正则表达式 USER1000="$(grep -Po "^[[:alnum:]-_]+(?=:x:1000:)" /etc/passwd)" # delete Ubuntu's first UID 1000 if [ -n "$USER1000" -a "$USER1000" != "adminbse" ]; then sed -i "s/$USER1000/adminbse/" /etc/group (sleep 25; deluser --remove-home $USER1000; delgroup $USER1000) & # delay until script ends so we don't get locked out fi
15. 选项 -l
,显示包含匹配内容的文件名
运维常用
- 默认显示所有匹配的内容,并在每一处匹配内容前显示文件名
1
2
3
4
5
6$ grep -s "^root" /etc/* /etc/group:root:x:0: /etc/passwd:root:x:0:0:root:/root:/bin/bash /etc/services:rootd 1094/tcp /etc/services:rootd 1094/udp
- 带
-l
选项仅显示包含匹配内容的文件名
1
2
3
4
5$ grep -sl "^root" /etc/* /etc/group /etc/passwd /etc/services
16. 选项 -L
,显示不匹配内容的文件名
运维常用
- 显示不包含匹配内容的文件名
1
2
3
4
5
6
7
8
9$ grep -sL "^root" /etc/* /etc/ImageMagick /etc/X11 /etc/acpi /etc/adduser.conf /etc/aliases /etc/aliases.db /etc/alternatives
3. 阅读代码时常用的 grep
操作
总结上一节中的各种选项,以下选项在阅读代码时最常用:
-i
,搜索结果是否忽略大小写-w
,搜索结果是否匹配完整单词-n
,搜索结果是否显示行号-C
,搜索结果是否显示匹配行的上下文--exclude
,不需要搜索的文件--exclude-dir
,不需要搜索的文件夹
千万不要想着为啥不包含正则表达式选项,其实我读代码使用正则表达式的情况特别特别少,一年也不需要几次,所以不用去记忆那些复杂的正则表达式规则。
大概 80% 的时间都是使用下面这三条 grep
命令搜索代码:
- 忽略大小写,可以匹配部分
1
2$ grep -Rni "keyword" dir/
- 忽略大小写,匹配完整单词
1
2$ grep -Rni "keyword" dir/
- 忽略大小写,排除部分文件(*.s, *.a)和目录(out 和 gen)
1
2$ grep -rni "keyword" dir/ --exlude=*.{s,a} --exlude-dir={out,gen}
阅读代码,准备搜索某个主题相关代码时,换位思考,想象你就是写代码的那个程序员,你会在代码中使用什么样的词?
然后就尝试用这个关键词使用 grep
进行搜索。这种方法非常好用,我在 《读代码,没有头绪怎么办?》 介绍过,希望你能喜欢这种思考方法。
下面列举几个常用的场景。
3.1 搜索字符串 “dynamic”
例如,想在 Android 的 build 目录查找动态分区相关的宏定义,但一时又不知道要用什么关键字,但动态分区,肯定和单词 dynamic
有关,于是可以在 build
目录下先搜索下所有带有 dynamic
的字符串。
因为只希望搜索 Makefile 相关的文件,所以这里:
- 排除不相关的其它文件
*.{py,go,css,js,html}
因为有些脚本文件做了链接,搜索结果可能会出现重复,我们这里:
- 使用
-r
选项不跟踪链接文件。
(如果使用 -R
选项则会搜索符号链接文件的内容)
所以执行的命令如下:
1
2$ grep -rni "dynamic" build/ --exclude=*.{py,go,css,js,html}
3.2 搜索字符串"V2"
在当前项目的 makefile (包括Makefile, makefile, *.mk, *.build, *.inc
等后缀)和一些脚本中搜索 V2
字符串。(secv2
)
在当前目录和子目录中搜索字符串 “V2
”:
- 仅搜索非代码文件
*.{h,c,cpp}
- 不在
obj.*
的目录中搜索 - 不搜索二进制文件
1
2$ grep -rnI V2 . --exclude=*.{h,c,cpp} --exclude-dir=obj.*
3.3 搜索字符串 “CMDLINE”
想知道命令行参数在 android 下是如何设置并起作用的?
在 Android 的 device
和 build
目录下搜索命令行参数的设置和使用,但是具体名字不记得了,那就搜索 CMDLINE 字符串吧。
在 device
和 build
目录中搜索字符串 “CMDLINE
”:
- 搜索
device
和build
目录 - 不想搜索
device/google
目录
1
2$ grep -rn CMDLINE device build --exclude-dir=google
3.4 搜索字符串 “retrofit”
在 Android 研究动态分区时,使用了一个字符串 “retrofit”,我想在 device
目录下查看各厂商对 “retrofit” 字符串相关的宏定义和引用,也想看下这些地方的上下文代码。
- 搜索
device
目录 - 不搜索
.git
目录 - 所有不分大小写的包含 “retrofit” 的字符串
- 查看匹配点的前后 5 行代码
1
2$ grep -Rni RETROFIT device/ --exclude-dir=.git -C 5
关于
grep
工具,如果你有什么好的用法,欢迎留言区或微信交流。
关于 grep 工具,如果你有什么好的用法,欢迎留言区或微信交流。
4. 近期文章
代码阅读
- 读代码,没有文档怎么办?
- 读代码,如何精准定位?
- 读代码,没有头绪怎么办?
- 读代码,多用用这种方式
调试
- 程序查错,一种排查问题的通用方法
- 程序查错,一类常见错误的处理思路
- 程序查错,第一件事要做什么?
其它
- 二八法则,致每一个前行路上的你
- 遇到有人装 X 显摆怎么办?
5. 其它
洛奇自己维护了一个公众号“洛奇看世界”,不定期瞎逼逼。公号也提供个人联系方式,一些资源,说不定会有意外的收获,详细内容见公号提示。扫下方二维码关注公众号:
最后
以上就是认真身影最近收集整理的关于一份资深程序员的 grep 笔记的全部内容,更多相关一份资深程序员的内容请搜索靠谱客的其他文章。
发表评论 取消回复