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

4.3.1 实战演练

grep命令作为Unix中用于文本搜索的神奇工具,能够接受正则表达式,生成各种格式的输出。

stdin中搜索匹配特定模式的文本行

  1. $ echo -e "this is a word\nnext line" | grep word
  2. this is a word
  1. [root@dev workspace]# echo -e "this is a word\nnext line"
  2. this is a word
  3. next line
  4. [root@dev workspace]# echo -e "this is a word\nnext line" | grep word
  5. this is a word
  6. [root@dev workspace]#

image.png

在文件中搜索匹配特定模式的文本行

  1. $ grep pattern filename
  2. this is the line containing pattern

或者

  1. $ grep "pattern" filename
  2. this is the line containing pattern

在多个文件中搜索匹配特定模式的文本行

  1. $ grep "match_text" file1 file2 file3 ...

重标记出匹配到的模式

选项--color可以在输出行中着重标记出匹配到的模式。尽管该选项在命令行中的放置位置没有强制要求,不过惯常作为第一个选项出现:

  1. $ grep --color=auto word filename
  2. this is the line containing word

扩展正则表达式

grep命令默认使用基础正则表达式。这是先前描述的正则表达式的一个子集。选项-E可以使grep使用扩展正则表达式。也可以使用默认启用扩展正则表达式的egrep命令:

  1. $ grep -E "[a-z]+" filename

或者

  1. $ egrep "[a-z]+" filename
  1. [root@dev workspace]# cat output.txt
  2. newline
  3. [root@dev workspace]# grep -e '[a-z]+' output.txt
  4. [root@dev workspace]# grep -e '[a-z]' output.txt
  5. newline
  6. [root@dev workspace]#

只显示匹配到的文本

选项-o可以只输出匹配到的文本:

  1. $ echo this is a line. | egrep -o "[a-z]+\."
  2. line
  1. [root@dev workspace]# echo this is a line. | egrep -o "[a-z]+\."
  2. line.
  3. [root@dev workspace]#

输出不匹配 match_pattern 的所有行

  1. $ grep -v match_pattern file

选项-v能够反转(invert)匹配结果。

  1. echo this is a line. | grep -v "[a-z]+\."
  1. [root@dev workspace]# echo this is a line. | grep -v "[a-z]+\."
  2. this is a line.
  3. [root@dev workspace]# echo this is a line. | grep -v "[a-z]\."
  4. [root@dev workspace]# echo this is a line. | grep -v "[a-z]+\."
  5. this is a line.
  6. [root@dev workspace]#

统计出匹配模式的文本行数

选项-c能够统计出匹配模式的文本行数:

  1. $ grep -c "text" filename
  2. 65
  1. [root@dev workspace]# grep -c "text" out.html
  2. 65
  3. [root@dev workspace]#

需要注意的是-c只是统计匹配行的数量,并不是匹配的次数。例如:

  1. $ echo -e "1 2 3 4\nhello\n5 6" | egrep -c "[0-9]"
  2. 2
  1. [root@dev workspace]# echo -e "1 2 3 4\nhello\n5 6" | egrep -c "[0-9]"
  2. 2
  3. [root@dev workspace]#

尽管有 6 个匹配项,但egrep命令只输出 2 ,这是因为只有两个匹配行。在单行中出现的多次匹配只被计为一次。

统计文件中匹配项的数量

要统计文件中匹配项的数量,可以使用下面的技巧:

  1. $ echo -e "1 2 3 4\nhello\n5 6" | egrep -o "[0-9]" | wc -l
  2. 6
  1. [root@dev workspace]# echo -e "1 2 3 4\nhello\n5 6" | egrep -o "[0-9]" | wc -l
  2. 6
  3. [root@dev workspace]#

输出出匹配字符串所在行的行号

选项-n可以打印出匹配字符串所在行的行号:

  1. $ cat sample1.txt
  2. gnu is not unix
  3. linux is fun
  4. bash is art
  5. $ cat sample2.txt
  6. planetlinux
  7. $ grep linux -n sample1.txt
  8. 2:linux is fun

或者

  1. $ cat sample1.txt | grep linux -n
  1. [root@dev workspace]# vi sample1.txt
  2. [root@dev workspace]# vi sample2.txt
  3. [root@dev workspace]# cat sample1.txt | grep linux -n
  4. 2:linux is fun
  5. [root@dev workspace]# cat sample1.txt
  6. gnu is not unix
  7. linux is fun
  8. bash is art
  9. [root@dev workspace]#

image.png
如果涉及多个文件,该选项也会随输出结果打印出文件名:

  1. $ grep linux -n sample1.txt sample2.txt
  2. sample1.txt:2:linux is fun
  3. sample2.txt:2:planetlinux
  1. [root@dev workspace]# vi sample1.txt
  2. [root@dev workspace]# vi sample2.txt
  3. [root@dev workspace]# cat sample1.txt | grep linux -n
  4. 2:linux is fun
  5. [root@dev workspace]# cat sample1.txt
  6. gnu is not unix
  7. linux is fun
  8. bash is art
  9. [root@dev workspace]# grep linux -n sample1.txt sample2.txt
  10. sample1.txt:2:linux is fun
  11. sample2.txt:1:planetlinux
  12. [root@dev workspace]#

输出匹配出现在行中的偏移

选项-b可以打印出匹配出现在行中的偏移。配合选项-o可以打印出匹配所在的字符或字节偏移:

  1. $ echo gnu is not unix | grep -b -o "not"
  1. [root@dev workspace]# echo gnu is not unix | grep -b -o "not"
  2. 7:not
  3. [root@dev workspace]#

字符在行中的偏移是从0开始计数,不是1

列出匹配模式所在的文件

选项-l可以列出匹配模式所在的文件:

  1. $ grep -l linux sample1.txt sample2.txt
  2. sample1.txt
  3. sample2.txt
  1. [root@dev workspace]# grep -l linux sample1.txt sample2.txt
  2. sample1.txt
  3. sample2.txt
  4. [root@dev workspace]#

-l效果相反的选项是-L,它会返回一个不匹配的文件列表。
image.png

4.3.2 补充内容

grep命令是Linux/Unix系统中最为全能的命令之一。它还包括其他一些选项,可用于搜索目录、选择待搜索的文件等。

递归搜索多个文件

如果需要在多级目录中对文本进行递归搜索,可以使用下列命令:

  1. $ grep "text" . -R -n

image.png
命令中的.指定了当前目录。例如:

  1. $ cd src_dir
  2. $ grep "test_function()" . -R -n
  3. ./miscutils/test.c:16:test_function();

grep的选项-R-r功能一样。

test_function()位于miscutils/test.c的第16行。如果你要在网站或源代码树中展开搜索,选项-R尤其有用。它等价于下列命令:

  1. $ find . -type f | xargs grep "test_function()"

忽略模式中的大小写

选项-i可以在匹配模式时不考虑字符的大小写:

  1. $ echo hello world | grep -i "HELLO"
  2. hello

使用grep匹配多个模式

选项-e可以指定多个匹配模式:

  1. $ grep -e "pattern1" -e "pattern2"

上述命令会打印出匹配任意一种模式的行,每个匹配对应一行输出。例如:

  1. $ echo this is a line of text | grep -o -e "this" -e "line"
  2. this
  3. line

image.png
可以将多个模式定义在文件中。选项-f可以读取文件并使用其中的模式(一个模式一行):

  1. $ grep -f pattern_filesource_filename

例如:

  1. $ cat pat_file
  2. hello
  3. cool
  1. $ echo hello this is cool | grep -f pat_file
  2. hello this is cool

grep搜索中指定或排除文件

grep可以在搜索过程中使用通配符指定(include)或排除(exclude)某些文件。

使用--include选项在目录中递归搜索所有的 .c.cpp文件:

  1. $ grep "main()" . -r --include *.{c,cpp}

注意,some{string1,string2,string3}会被扩展成somestring1somestring2somestring3

使用选项--exclude在搜索过程中排除所有的README文件:

  1. $ grep "main()" . -r --exclude "README"

选项--exclude-dir可以排除目录:

  1. $ grep main . -r -exclude-dir CVS

如果需要从文件中读取排除文件列表,使用--exclude-from FILE

使用 0 值字节后缀的 xargs 与 grep

xargs命令可以为其他命令提供命令行参数列表。当文件名作为命令行参数时,建议用0值字节作为文件名终结符,而非空格。因为一些文件名中会包含空格字符,一旦它被误解为终结符,那么单个文件名就会被视为两个(例如,New file.txt被解析成Newfile.txt两个文件名)。这个问题可以利用0值字节后缀来避免。我们使用xargs从命令(如grepfind)中接收stdin文本。这些命令可以生成带有0值字节后缀的输出。为了指明输入中的文件名是以0值字节作为终结,需要在xargs中使用选项-0

创建测试文件:

  1. $ echo "test" > file1
  2. $ echo "cool" > file2
  3. $ echo "test" > file3

选项-l告诉grep只输出有匹配出现的文件名。选项-Z使得grep使用0值字节(\0)作为文件名的终结符。这两个选项通常都是配合使用的。xargs-0选项会使用0值字节作为输入的分
隔符:

  1. $ grep "test" file* -lZ | xargs -0 rm

image.png

grep 的静默输出

有时候,我们并不打算查看匹配的字符串,而只是想知道是否能够成功匹配。这可以通过设置grep的静默选项(-q)来实现。在静默模式中,grep命令不会输出任何内容。它仅是运行命令,然后根据命令执行成功与否返回退出状态。0表示匹配成功,非0表示匹配失败。

下面这个脚本利用grep的静默模式来测试文件中是否有匹配文本:

  1. #!/bin/bash
  2. #文件名: silent_grep.sh
  3. #用途:测试文件是否包含特定的文本内容
  1. if [ $# -ne 2 ]; then
  2. echo "Usage: $0 match_text filename"
  3. exit 1
  4. fi
  5. match_text=$1
  6. filename=$2
  7. grep -q "$match_text" $filename
  8. if [ $? -eq 0 ]; then
  9. echo "The text exists in the file"2
  10. else
  11. echo "Text does not exist in the file"
  12. fi

这个silent_grep.sh脚本接受两个命令行参数:一个是需要匹配的单词(Student),另一个是文件名(student_data.txt):

  1. $ ./silent_grep.sh Student student_data.txt
  2. The text exists in the file

打印出匹配文本之前或之后的行

基于上下文的打印是grep的一个挺不错的特性。当grep找到了匹配模式的行时,它只会打印出这一行。但我们也许需要匹配行之前或之后的n行。这可以通过控制选项-B-A来实现。

选项-A可以打印匹配结果之后的行:

  1. $ seq 10 | grep 5 -A 3
  2. 5
  3. 6
  4. 7
  5. 8

image.png
选项-B可以打印匹配结果之前的行:

  1. $ seq 10 | grep 5 -B 3
  2. 2
  3. 3
  4. 4
  5. 5

image.png
选项-A-B可以结合使用,或者也可以使用选项-C,它可以分别打印出匹配结果之前及之后的n行:

  1. $ seq 10 | grep 5 -C 3
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8

image.png
如果有多个匹配,那么使用--作为各部分之间的分隔:

  1. $ echo -e "a\nb\nc\na\nb\nc" | grep a -A 1
  2. a
  3. b
  4. --
  5. a
  6. b

image.png