Shell 使用grep在文件中搜索文本,如果你忘记把钥匙放在了哪里,就得自己去找;如果你忘记了文件中的内容,grep命令可以帮助你查找。本章将教你如何定位包含特定文本模式的文件。

实战演练
grep命令作为Unix中用于文本搜索的神奇工具,能够接受正则表达式,生成各种格式的输出。
(1) 在stdin中搜索匹配特定模式的文本行:
$ echo -e "this is a word\nnext line" | grep word
this is a word
(2) 在文件中搜索匹配特定模式的文本行:
$ grep pattern filename
this is the line containing pattern
或者
$ grep "pattern" filename
this is the line containing pattern
(3) 在多个文件中搜索匹配特定模式的文本行:
$ grep "match_text" file1 file2 file3 ...
(4) 选项--color可以在输出行中着重标记出匹配到的模式。尽管该选项在命令行中的放置位置没有强制要求,不过惯常作为第一个选项出现:
$ grep --color=auto word filename
this is the line containing word
(5) grep命令默认使用基础正则表达式。这是先前描述的正则表达式的一个子集。选项-E可以使grep使用扩展正则表达式。也可以使用默认启用扩展正则表达式的egrep命令:
$ grep -E "[a-z]+" filename
或者
$ egrep "[a-z]+" filename
(6) 选项-o可以只输出匹配到的文本:
$ echo this is a line. | egrep -o "[a-z]+\."
line
(7) 选项-v可以打印出不匹配match_pattern的所有行:
$ grep -v match_pattern file
选项-v能够反转(invert)匹配结果。
(8) 选项-c能够统计出匹配模式的文本行数:
$ grep -c "text" filename
10
需要注意的是-c只是统计匹配行的数量,并不是匹配的次数。例如:
$ echo -e "1 2 3 4\nhello\n5 6" | egrep  -c "[0-9]"
2
尽管有6个匹配项,但egrep命令只输出2,这是因为只有两个匹配行。在单行中出现的多次匹配只被计为一次。
(9) 要统计文件中匹配项的数量,可以使用下面的技巧:
$ echo -e "1 2 3 4\nhello\n5 6" | egrep -o "[0-9]" | wc -l
6
(10) 选项-n可以打印出匹配字符串所在行的行号:
$ cat sample1.txt
gnu is not unix
linux is fun
bash is art
$ cat sample2.txt
planetlinux
$ grep linux -n sample1.txt
2:linux is fun
或者
$ cat sample1.txt | grep linux -n
如果涉及多个文件,该选项也会随输出结果打印出文件名:
$ grep linux -n sample1.txt sample2.txt
sample1.txt:2:linux is fun
sample2.txt:2:planetlinux
(11) 选项-b可以打印出匹配出现在行中的偏移。配合选项-o可以打印出匹配所在的字符或字节偏移:
$ echo gnu is not unix | grep -b -o "not"
7:not
字符在行中的偏移是从0开始计数,不是1。
(12) 选项-l可以列出匹配模式所在的文件:
$ grep -l linux sample1.txt sample2.txt
sample1.txt
sample2.txt
和-l效果相反的选项是-L,它会返回一个不匹配的文件列表。
补充内容
grep命令是Linux/Unix系统中最为全能的命令之一。它还包括其他一些选项,可用于搜索目录、选择待搜索的文件等。
- 递归搜索多个文件
 
如果需要在多级目录中对文本进行递归搜索,可以使用下列命令:
$ grep "text" . -R -n
命令中的.指定了当前目录。例如:
$ cd src_dir
$ grep "test_function()" . -R -n
./miscutils/test.c:16:test_function();
grep的选项-R和-r功能一样。
test_function()位于miscutils/test.c的第16行。如果你要在网站或源代码树中展开搜索,选项-R尤其有用。它等价于下列命令:
$ find . -type f | xargs grep "test_function()"
- 忽略模式中的大小写
 
选项-i可以在匹配模式时不考虑字符的大小写:
$ echo hello world | grep -i "HELLO"
hello
- 使用
grep匹配多个模式 
选项-e可以指定多个匹配模式:
$ grep -e "pattern1" -e "pattern2"
上述命令会打印出匹配任意一种模式的行,每个匹配对应一行输出。例如:
$ echo this is a line of text | grep -o -e "this" -e "line"
this
line
可以将多个模式定义在文件中。选项-f可以读取文件并使用其中的模式(一个模式一行):
$ grep -f pattern_filesource_filename
例如:
$ cat pat_file
hello
cool
$ echo hello this is cool | grep -f pat_file
hello this is cool
- 在
grep搜索中指定或排除文件 
grep可以在搜索过程中使用通配符指定(include)或排除(exclude)某些文件。
使用--include选项在目录中递归搜索所有的 .c和 .cpp文件:
$ grep "main()" . -r  --include *.{c,cpp}
注意,some{string1,string2,string3}会被扩展成somestring1 somestring2 somestring3。
使用选项--exclude在搜索过程中排除所有的README文件:
$ grep "main()" . -r --exclude "README"
选项--exclude-dir可以排除目录:
$ grep main . -r -exclude-dir CVS
如果需要从文件中读取排除文件列表,使用--exclude-from FILE。
- 使用0值字节后缀的
xargs与grep 
xargs命令可以为其他命令提供命令行参数列表。当文件名作为命令行参数时,建议用0值字节作为文件名终结符,而非空格。因为一些文件名中会包含空格字符,一旦它被误解为终结符,那么单个文件名就会被视为两个(例如,New file.txt被解析成New和file.txt两个文件名)。这个问题可以利用0值字节后缀来避免。我们使用xargs从命令(如grep和find)中接收stdin文本。这些命令可以生成带有0值字节后缀的输出。为了指明输入中的文件名是以0值字节作为终结,需要在xargs中使用选项-0。
创建测试文件:
$ echo "test" > file1
$ echo "cool" > file2
$ echo "test" > file3
选项-l告诉grep只输出有匹配出现的文件名。选项-Z使得grep使用0值字节(\0)作为文件名的终结符。这两个选项通常都是配合使用的。xargs的-0选项会使用0值字节作为输入的分隔符:
$ grep "test" file* -lZ | xargs -0 rm
grep的静默输出
有时候,我们并不打算查看匹配的字符串,而只是想知道是否能够成功匹配。这可以通过设置grep的静默选项(-q)来实现。在静默模式中,grep命令不会输出任何内容。它仅是运行命令,然后根据命令执行成功与否返回退出状态。0表示匹配成功,非0表示匹配失败。
下面这个脚本利用grep的静默模式来测试文件中是否有匹配文本:
#!/bin/bash
# 文件名: silent_grep.sh
# 用途: 测试文件是否包含特定的文本内容
if [ # -ne 2 ]; then
  echo "Usage:0 match_text filename"
  exit 1
fi
match_text=1
filename=2
grep -q "match_text"filename
if [ $? -eq 0 ]; then
  echo "The text exists in the file"
else
  echo "Text does not exist in the file"
fi
这个silent_grep.sh脚本接受两个命令行参数:一个是需要匹配的单词(Student),另一个是文件名(student_data.txt):
$ ./silent_grep.sh Student student_data.txt
The text exists in the file
- 打印出匹配文本之前或之后的行
 
基于上下文的打印是grep的一个挺不错的特性。当grep找到了匹配模式的行时,它只会打印出这一行。但我们也许需要匹配行之前或之后的n行。这可以通过控制选项-B和-A来实现。
选项-A可以打印匹配结果之后的行:
$ seq 10 | grep 5 -A 3
5
6
7
8
选项-B可以打印匹配结果之前的行:
$ seq 10 | grep 5 -B 3
2
3
4
5
选项-A和-B可以结合使用,或者也可以使用选项-C,它可以分别打印出匹配结果之前及之后的n行:
$ seq 10 | grep 5 -C 3
2
3
4
5
6
7
8
如果有多个匹配,那么使用--作为各部分之间的分隔:
$ echo -e "a\nb\nc\na\nb\nc" | grep a -A 1
a
b
--
a
b
极客教程