竹笋

首页 » 问答 » 问答 » grep查找日志关键字并高亮及显示
TUhjnbcbe - 2025/3/4 17:48:00

对于一个开发或运维人员而言,当系统出现故障时,第一步常常就是查看日志.查看日志经常碰到的一个需求就是按关键字去搜索,在日常开发机子上的IDE上,都集成了强大的搜索功能,但因为系统通常部署在Linux系统上,一般只有命令行界面,在其上应该怎么去搜索呢?恐怕有些同学就不是那么清楚了.

有些人会用ftp之类的把日志下载下来本地再搜索,如果是小一点的文件还好,但日志文件往往都比较大,因此这样的方式无疑是极为低效的.

下面就介绍一种相对快捷的方式,也不需要用到特别高级的命令,仅需要tail和grep两个命令结合起来即可,能达到这样一个效果:

能按关键字搜索;在显示关键字所在行时还能高亮关键字;能把关键字所在行的上下文,比如上下10行的内容也一起显示出来.下面是一个效果示意图:

在这里,我用我云主机的nginxaccesslog做了个示范,我搜索一篇文章url的关键字a-port,然后显示出搜索的结果及上下文,可以看到关键字被标红显示,上下文也有显示,多个搜索结果间以蓝色的短横间隔开来.

下面具体说说怎么实现这样的搜索,先具体讲讲各个命令及参数,再说说怎么结合起来,最后还给出一个脚本化的高级用法.

tail命令

首先是tail命令.因为查看日志通常从后面最新的日志去看,tail命令就是从后往前找.

比如下述命令会显示access.log的最后10行的内容:

tailaccess.log

tail指定行数

默认情况下,tail只会显示最后的10行,对于一个日志很多的应用来说,这可能是不够的,为此我们需要搜索更多的行.

如果想实时查看日志,可以参考之前的这篇文章使用tail-f实时观测服务器日志输出

tail可以结合-n参数指定一个行数,比如下述命令会显示最后的30行的日志:

tail-n30access.log

注:如果不太能记住参数,还可以使用-n的完整命令参数--lines:tail--lines30access.log

grep命令

tail仅能打印显示日志,很多时候这是不够的,日志通常非常多,而且很多是没有用,我们还需要能过滤,或者说搜索筛选日志的内容,这时就可以使用grep命令.

grep命令的基本用法是这样的.假如你有一个文件index.html,你想在其中搜索一个关键词official,你可以这样用:

grepofficialindex.html

结果如下:

它会把关键字所在行给你显示出来,并高亮关键字.

关于高亮问题,如果缺省没有高亮,则可以自行加入--color选项,像这样:grep--colorofficialindex.html

注意,通常不要直接用grep命令去搜索整个日志文件,因为日志文件通常很大,而且grep也是从开头开始搜索的,因此可能搜索出一大堆你不感兴趣的历史记录.

后面将介绍如何结合tail和grep命令以缩小搜索范围.

带有空格的关键字

如果关键字有多个单词并带有空格,可以使用单引号引起来,例如:

grepenglishversionindex.html

grep显示上下文

有时,我们不但要找出关键子所在行,而且还想显示所在行上下的一些行.

这在查找异常信息时非常常见,一方面异常栈会打印成非常多行,另外我们通常需要前前后后都看一下到底发生了什么.

这时可以使用grep的-NUM参数来实现,如下:

grep-5officialindex.html

或者是使用-C

grep-C5officialindex.html

它表示,不但要找出official关键字所在行,还要把所在行前后的5行都显示出来.

后接的数字5就表示前后5行,如果是-10就表示前后10行

结果如下:

grep显示行号

为了更清晰地呈现,还可以选择显示行号,用-n参数,如下:

grep-5-nofficialindex.html

结果如下图:

可以看到,official关键字在21行,行头的行号还特别以冒号:标出;此外,关键行的前5行(16~20)和后5行(22~26)也一并显示了出来.

如果我们是在跟踪一个异常,这些上下文的信息可能会提供很多帮助.

管道符的使用

现在已经介绍完了tail和grep命令,但还有一个问题,如果直接在日志文件中去grep的话,因为文件通常特别大,而且很多历史数据可能不是我们想要的,因此最好的方式是先用tail得到后面的那些行,然后把tail出来的结果再交给grep命令去过滤,而管道符可以实现这个目的,管道符在命令行中就是一个竖杠:

.

它可以把两个命令结合起来,把请一个命令的输出当作后一个命令的输入.

用管道符

结合tail和grep命令

用管道符结合tail和grep命令可以这样去写:

tailerror.log

grepstream

注意:grep之前的竖杠

.

上述命令会把tail出来的最后10行的内容交给grep去搜索过滤,并找出其中含有stream关键字的行,结果如下:

结合前面所讲,如果想在更大范围搜索并显示关键字的上下文,最终可以这样去写:

tail-n20error.log

grep-3stream

以上命令在最后20行中去搜索stream关键字并显示关键字所在行及上下各3行的内容,结果如下:

高级用法

有了以上命令,要搜索异常信息就简单了不少,而且更容易观察,不过还是有一个问题,就是整个命令还是太长了些,如果想进一步简化,则可以考虑将整个命令做成一个脚本,并将部分参数值参数化,这就带有一定的编程的味道了,好在这对于我们程序员来说,不算太难的事,甚至是我们的日常,下面说说怎么去实现.

先说下效果,我们会编写一个脚本叫search.sh

当然这个名字你可以自己去取

然后这样去用:

./search.shstream

然后其效果就像执行下述命令一样:

tail-n20error.log

grep-3stream

如果不打算传入行数及上下文的数目,而使用脚本中定义的缺省值,整个命令还可以简化成:

./search.shstream

仅需要传入要搜索的关键字即可,其它参数保持缺省.

自定义命令

就以搜索我本机上的nginx的error.log为例吧,首先创建一个脚本文件search.sh

touchsearch.sh

文件的内容如下:

#!/bin/bashcd/usr/local/nginx/logstail-n20error.log

grep--color-3stream

注意:放入脚本文件时,如果没有高亮,需要自行加上--color选项

逻辑也比较简单,就是先进入error.log所在文件夹,然后执行查找.

有了cd命令,就可以直接把脚本放在远程登录后的用户目录下,比如/root下,这样进去了就可以直接执行,连进入文件夹的动作也省略了.

另外,如果不想用cd命令,也可以在tail中写上完整路径名.

当然,现在脚本还是比较死的,搜索的关键字被写死了.不过目前来说,我们先测试其它方面,先把文件改成可执行的:

chmodsearch.sh

然后可以先执行一遍看看是否ok,如果ok了,再下一步准备把关键字参数化.

./search.sh

参数传递

现在需要把搜索的关键字给参数化,不然执行脚本时,始终只能搜索stream这个关键字,这显然不是我们希望的.

如果是用我们熟悉的语言,比如java,javascript,写一个可以接收参数的函数是很简单的,其实对于bash这种脚本语言来说,主要的问题是我们不熟悉其语法,这个只要稍微查下它的手册或是在网上搜索下即不难知道.

过程就不提了,具体而言是这样的:

#!/bin/bashcd/usr/local/nginx/logstail-n20error.log

grep--color-3$1

就是把stream这个写死的关键字变成一个变量$1,自然$符号就是bash跟定义变量有关的.

自然,你应该能猜到,如果想传递更多的参数,就用$2,$3,以此类推.

然后你这样

./search.shhello

那么脚本文件名后面跟的字符串hello就会传递给$1这个变量,于是就相当于执行了:

tail-n20error.log

grep--color-3hello

同理,可以把tail的行数和grep的上下文的行数也参数化:

#!/bin/bashcd/usr/local/nginx/logstail-n$2error.log

grep--color-$3$1

如此一来,当执行下述命令时:

./search.shhello

就相当于:

tail-nerror.log

grep--color-10hello

也即在日志文件的最后行里搜索,并显示关键行上下各10行的内容.

缺省值及判断逻辑

自然,很多时候可能只想传递关键字即可,当把tail的行数和grep的上下文的行数也参数化后,每次调用也要传递它们是不方便的,当如果把它们写死的话,有时我们可能又需要适当变化,这个矛盾怎么解决呢?答案是利用缺省值和逻辑判断.

如果是常用的语言,如java,javascript,写个这种判断相信对你来说是个再简单不过的事,对于bash这种脚本语言,最大的问题还是我们不熟悉其语法,那么这个还是跟之前说的那样,查查手册,或搜索下,过程就省略了,具体来说,可以这样:

简单说就是定义两个变量lineCount和contextCount,分别具有和10两个缺省值,然后利用if判断用户是否输入了第二和第三个参数,如果有,就用它们的值取代缺省值,没有的话就使用缺省值,这样一来就比较灵活了.

如果只输入了关键字:

./search.shhi

结果就是这样:

tail-nerror.log

grep--color-10hi

输入两个参数:

./search.shhello

结果就是这样:

tail-nerror.log

grep--color-10hello

输入三个参数:

./search.shhey

结果就是这样:

tail-nerror.log

grep--color-8hey

当然还是有个问题,当你想只调整第三个参数时,你还是必须得传入第二个参数,否则传入的值只会被第二个参数优先获得.

命令输出

最后,如果你想在执行前回显一下将要执行的命令,还可以利用echo这个命令来实现,它同样支持变量:

这样一来,执行前就会先打印出将要执行的命令.

总结

综上所述,从单个命令到复合命令,再到脚本化和参数化,其实是用了编程中的抽象这一手法,这是我们解决重复性以及解决复杂性的一种重要手段.

当一个命令或几个的复合命令比较繁琐时,我们就用一个脚本文件去做抽象,保留不变的东西,把变化的东西参数化,外部化,通过这样的方式,就简化了执行(调用)的过程,减少了重复.

毕竟,如果你经常需要查找日志的话,输入简单的./search.shfoo比反复输入如此之长的一串tail-nerror.log

grep--color-10foo要方便快捷的多.

1
查看完整版本: grep查找日志关键字并高亮及显示