linux 提供了强大的文本分析工具,如 grep, sed, awk,号称三剑客。本篇介绍文本查找命令 grep

grepglobal regular expression print)命令能够过滤出文本中符合要求(如正则表达式)的内容、行、文件等。默认在所有 linux 发行版中都预装该命令。

基本命令格式

1
grep [options|flags] pattern file1 file2 

如:

1
2
3
4
5
6
# 查找系统上某个用户(如 root)的默认登录 shell
# 这里省略了 options 或 flags
# pattern 是 "root",pattern 可以用正在表达式,建议用引号包裹
# /etc/passwd 是文件名. grep 后面可以跟多个文件名
# | 管道符是把 grep 输出的一行内容按照 : 分割为多列,只打印最后一列
grep "root" /etc/passwd | awk -F ":" '{print $NF}'

options

options 有时也叫作 flags,是控制 grep 命令输出内容的参数。下面罗列一些常用的参数,更多请帮助文档:man grep

  • –color=always, –color=auto, –color=never: 是否以不同颜色显示查找到的文本内容
  • -a, –text:不要忽略二进制数据。用于从二进制文件中查找内容。如果查找二进制文件里的内容,请添加该参数:grep -a "合并" binary_files,查找二进制文件中是否包含有 “合并”
  • -A num, –after-context=num:对于查找到的某一行同时打印该行后 num 行内容,常用于日志分析,如打印报错内容的后面内容:grep -A 3 "error" run.log,查找 run.log 中包含有 error 行及其后 3 行
  • -B num, –before-context=num:与 -A num 相反,打印某一行的前 num 行:grep -B 3 "error" run.log,查找 run.log 中包含有 error 行及其前 3 行
  • -C num, –context=num:同时包含 -A num 和 -B num,表示打印找到的某一行的前后各 num 行:grep -C 3 "error" run.log,查找 run.log 中包含有 error 行及其前后 3 行
  • -c:只打印匹配的行数,不打印查找到的文本内容
  • -e pattern, –regexp=pattern:指定字符串作为查找文件内容的范本样式,这里字符串可以是正则表达式(下面介绍),可以指定多个 -e,用于查找多个内容:grep -e "root" -e "bash$" /etc/passwd,查找 root,以及以 bash 结尾的行
  • -E pattern, –extended-regexp=pattern:扩展正则表达式,即 egrep,指定可以使用扩展的正则表达式(下面介绍),示例:grep -E "ro+t" /etc/passwd,查找 root, rooot, …
  • -i, –ignore-case:忽略字符大小写,如同时查找 h 和 H:grep -i "h" /etc/passwd,查找 /etc/passwd 中包含有 h,H 的行
  • -l, –file-with-matches: 列出文件内容符合指定的 pattern 的文件名称: grep -l "root" /etc/*,查找 /etc 目录下所有包含 root 文本内容的文件名
  • -L, –files-without-match: 列出文件内容不符合指定的 pattern 的文件名称: grep -L "root" /etc/*,列文件夹 /etc 下不包含文本内容 root 的所有文件名
  • -n, –line-number: 打印找到的那一行内容前增加上行号:grep -n "root" /etc/passwd
  • -q, –quiet或–silent:不显示任何信息
  • -R/-r, –recursive: 递归搜索文件夹里面的文件,与 -d recurse 参数含义相同。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # 递归搜索 /etc 目录下所有文件包含 root 内容的行
    grep -r "root" /etc

    # 递归搜索 ./codes 目录下所有以 py,sh,php,html,txt,md 结尾的文件里含有 sam 的行
    grep -r --include=*.{py,sh,php,html,txt,md} "sam" ./codes

    # 递归搜索 ./codes 目录下所有包含 sam 的文件行,但不包含以 .json 结尾的文件
    grep -r --exclude=.json "sam" ./codes

    # 递归搜索 ./codes 目录下所有文件包含 sam 的文件行,但是排除文件 filelist 中列出的所有文件
    grep -r --exclude-from=filelist "sam" ./codes

    # 递归搜索 ./codes 目录下所有文件包含 sam 的文件行,但是排除文件夹 py
    # py 可以是 codes 下的文件夹,也可以是其下子文件夹里的文件夹,但只需要指定文件夹名即可,不需要写相对或绝对路径
    grep -r --exclude-dir=py "sam" ./codes
  • -s, –no-messages:不显示错误信息
  • -v, –revert-match:反转查找,即对于匹配到的 pattern 不显示,如:ps ef | grep python | grep -v grep,查找 python 进程,但是不显示 grep 查找自己的进程
  • -V, –version:显示版本信息
  • -o:只输出文件中匹配到的部分,不打印行自打印查找到的文本内容本身
  • -m , –max-count=:找到 num 行结果后停止查找,用来限制匹配行数

例子:

1
2
# 查找文件中有多少个 sam 文本内容
grep -i "sam" image_sam_annotate.py -o -c

pattern

pattern 可以是文本内容,也可以是正则表达式,或扩展正则表达式。

正则表达式

使用正则表达式:

1
2
# 可以指定多个 -e
grep -e "xxx" filepath
符号 含义
^ 锚定行的开始,如 “^import” 匹配所有以 import 开头的行
$ 锚定行的介绍,如 “p$” 匹配所有以 p 结尾的行
^$ 匹配空白行
. 匹配一个非换行符的字符,如 “impo.t” 可以匹配 import,impoot, …
* 匹配零个或多个前一个字符,如 “1*” 匹配 1 后零个或多个 1
.* . 和 * 一起使用可以匹配任意字符
[] 匹配一个指定范围内的字符,如 “[a-z]” 匹配字符 a 到 z 的所有单个小写字符,”[0-9a-zA-Z]” 匹配所有单个数字或字符
[^] 与 [] 相反,匹配一个不在指定范围外的字符,如 “[^0-57-9]” 只匹配数字 6
\< 锚定单词的开始,如 “<np” 或 “\bnp”,匹配某行中独立的以np开头的单词,不一定在行首
\> 锚定单词的结束,如 “np>“ 或 “np\b”,匹配某行中独立的以 np 结尾的单词,不一定在行尾
\w 匹配一个英文字符或数字,同[0-9a-zA-Z]
\W 与 \w 相反,匹配除了英文字符和数字外的单个字符
\b 单词锁定,”\bas\b” 或者 “<as>“ 只匹配单词 as

Linux 系统中默认在每行行尾添加上符号 $,可以通过命令 cat -A file 查看

示例:

1
2
3
4
# 表示匹配一个数字+b组成的字符对
grep -e "[0-9]b" file

# 如果 -e 想要使用下面的扩展正则,需要增加反斜线 \,如 \?, \{m,n\}, \(x\)s

扩展正则表达式

扩展正则表达式(ERE,Extended Regular Expression),在正则表达式的基础上增加上 {}()?+|

1
grep -E "xxx" file
符号 含义
+ 匹配前面的一个字符 1 次或多次
? 匹配其前面的字符 1 次或 0 次,使用 grep “a?b” file, 匹配 b, ab
x{m} 重复字符 x 正好 m 次
x{m,} 重复字符 x 至少 m 次
x{m,n} 重复字符 x 至少 m 次,至多 n 次,默认以贪婪匹配,匹配字符尽量多
(x) 标记匹配字符,x 内容被标记为 \1
| | 表示或,如 a|b 表示匹配 a 或者 b

示例:

1
2
3
4
5
6
7
8
9
# 表示 ab 整体作为匹配字符,且匹配至少一次
grep -E "(ab){1,}" file

# \1 引用第一个左括号, \2 引用第二个左括号
grep -E "(l..e).*\1r" file

# 匹配结果如下:
He love his lover.
She like her liker.

POSIX字符

为了在不同国家的字符编码中保持一至,POSIX(The Portable Operating System Interface)增加了特殊的字符类,如 [:alnum:] 是 [A-Za-z0-9] 的另一个写法。要把它们放到 [] 号内才能成为正则表达式,如 [A- Za-z0-9] 或 [[:alnum:]]。在 linux 下的 grep 除 fgrep 外,都支持 POSIX 的字符类。

  • [:alnum:] # 文字数字字符,表示所有字母和数字 [0-9a-zA-Z]
  • [:alpha:] # 文字字符,表示所有字母(包含大小写) [a-zA-Z]
  • [:digit:] # 数字字符,所有数字 [0-9]
  • [:graph:] # 非空字符(非空格、控制字符)
  • [:lower:] # 小写字符,所有小写字母 [a-z]
  • [:cntrl:] # 控制字符
  • [:print:] # 非空字符(包括空格)
  • [:punct:] # 标点符号,所有标点符号
  • [:space:] # 所有空白字符(新行,空格,制表符), 表示空格或tab键
  • [:upper:] # 大写字符,所有大写字母 [A-Z]
  • [:xdigit:] # 十六进制数字(0-9,a-f,A-F)

退出状态

item description
0 A match was found
1 No match was found
>1 A syntax error was found or a file was inaccessible (even if matches were found)

与其他命令结合

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
# echo 
echo "hello, world!" | grep hello

# tail
tail -f x.log | grep 2024

# ps
ps -aux | grep python | grep -v grep
ps -ef | grep python | grep -v grep

# wc, 输出 行数(wc -l),单词数(wc -w),字节数(wc -m )
# 字符数(wc -c,注意结尾$也算一个字符)
echo "hello, world" | wc

# xargs
find ~ -name "*.py" -print | xargs grep "import"

# awk
ps aux | grep "python" | grep -v grep | awk '{print "kill -9" $2}' | sh
ps aux | grep "python" | grep -v grep | awk '{print $2}' | xargs kill -9
kill -9 `ps aux | grep "python" | grep -v grep | awk '{print $2}'`

# 其他杀进程根据程序名
pgrep "python" | xargs kill -s 9
kill -o `pgrep "python"`
# pgrep "python" 等价于上面的 ps aux | grep "python" | grep -v grep | awk '{print $2}'

pkill -9 "python" # 名字不需要全名
pkill -u admin # 删掉用户 admin 运行的所有进程

killall "python" # 名字需要全名

参考文献

  1. Linux三剑客超全超详情教程(grep、sed、awk入门到精通有这一套足够了)
  2. Linux文本处理三剑客:Grep/Sed/Awk详解
  3. grep扩展正则表达式(egrep)
  4. bash基础——grep、基本正则表达式、扩展正则表达式、fgrep
  5. Linux grep 命令
  6. grep
  7. 每天一个linux命令(39):grep 命令