我是靠谱客的博主 认真身影,这篇文章主要介绍一份资深程序员的 grep 笔记,现在分享给大家,希望可以做个参考。

本文原创发布于微信公众号“洛奇看世界”

网上搜索 grep 的结果基本上都是一些跟运维相关的操作,并不适用于阅读代码。

当你不想使用复杂的代码阅读工具时,快速查找代码,使用 grep 非常方便。

这里跟大家分享一下我的 grep 笔记。

这个 grep 笔记一开始比较简单,但随着各种操作的需要,出现了新的知识点,然后不断迭代,几年后差不多就是现在这个样子。

懒得整理了,除了在第三部分临时增加了两个例子之外,真的就是我自己的 grep 笔记。

本文主要包含 3 个部分:

  • 第一部分总结了 grep 命令的一些注意事项;
  • 第二部分汇总了 grep 命令的多种选项用法;
  • 第三部分总结我用 grep 看代码用得最多的方法;

除了用于阅读代码之外,平时管理服务器也经常用 grep 命令,所以对各个命令常用的场景做了标记,包括看代码常用运维常用两种。

1. grep 的命令格式

复制代码
1
2
3
# grep [选项] 搜索模式 [文件和目录列表] grep [OPTION]... PATTERN [FILE]...

1.1 注意事项

  • 引号问题

建议: 三剑客 grep,sedawk 的模式字符串都使用单引号 ‘’ 包含

搜索的模式 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 操作会返回其状态,对于一般函数来讲,就是函数内部的 returnexit 等。
返回值通过变量 $? 获取。

grep 的man手册中是这样定义返回值的:

复制代码
1
2
3
4
5
EXIT 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
6
if [ "$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
12
bolt-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
16
if ! 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 的 devicebuild 目录下搜索命令行参数的设置和使用,但是具体名字不记得了,那就搜索 CMDLINE 字符串吧。

devicebuild 目录中搜索字符串 “CMDLINE”:

  • 搜索 devicebuild 目录
  • 不想搜索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 笔记的全部内容,更多相关一份资深程序员的内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(84)

评论列表共有 0 条评论

立即
投稿
返回
顶部