对于一个开发或运维人员而言,当系统出现故障时,第一步往往就是查看日志.查看日志常常遇到的一个需求就是按关键字去搜索,在日常开发机子上的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命令.
grep命令的基本用法是这样的.如果你有一个文件index.html,你想在其中搜索一个关键词official,你可以这样用:
grepofficialindex.html
结果如下:
它会把关键字所在行给你显示下来,并高亮关键字.
关于高亮问题,倘若缺省没有高亮,则可以自行加入--color选项,像这样:grep--colorofficialindex.html
注意,一般不要直接用grep命令去搜索整个日志文件,由于日志文件一般很大,并且grep也是从开头开始搜索的,因而可能搜索出一大堆你不感兴趣的历史记录.
前面将介绍怎样结合tail和grep命令以缩小搜索范围.
带有空格的关键字
假如关键字有多个词组并带有空格,可以使用''单冒号造成来,比如:
grep'englishversion'index.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.shstream203
之后其疗效如同执行下列命令一样:
tail-n20error.log|grep-3stream
若果不准备传入行数及上下文的数量,而使用脚本中定义的缺省值,整个命令还可以简化成:
./search.shstream
仅须要传入要搜索的关键字即可,其它参数保持缺省.
自定义命令
就以搜索我本机上的nginx的error.log为例吧,首先创建一个脚本文件search.sh
touchsearch.sh
文件的内容如下:
#!/bin/bash
cd/usr/local/nginx/logs
tail-n20error.log|grep--color-3stream
注意:装入脚本文件时,假如没有高亮,须要自行加上--color选项
逻辑也比较简单,就是先步入error.log所在文件夹,之后执行查找.
有了cd命令,就可以直接把脚本置于远程登陆后的用户目录下,例如/root下,这样进去了就可以直接执行,连步入文件夹的动作也省略了.
另外,倘若不想用cd命令,也可以在tail中写上完整路径名.
其实,如今脚本还是比较死的,搜索的关键字被写死了.不过目前来说,我们先测试其它方面,先把文件改成可执行的:
chmod755search.sh
之后可以先执行一遍瞧瞧是否ok,倘若ok了,再下一步打算把关键字参数化.
./search.sh
参数传递
如今须要把搜索的关键字给参数化,不然执行脚本时,一直只能搜索'stream'这个关键字,这其实不是我们希望的.
假如是用我们熟悉的语言,例如java,javascript,写一个可以接收参数的函数是很简单的,虽然对于bash这些脚本语言来说,主要的问题是我们不熟悉其句型,这个只要稍稍查下它的指南或是在网上搜索下即不难晓得.
过程就不提了,具体而言是这样的:
#!/bin/bash
cd/usr/local/nginx/logs
tail-n20error.log|grep--color-3$1
就是把stream这个写死的关键字弄成一个变量$1,自然$符号就是bash跟定义变量有关的.
自然,你应当能猜到,假如想传递更多的参数,就用$2,$3,以这种推.
之后你这样
./search.shhello
这么脚本文件名前面跟的字符串'hello'都会传递给$1这个变量,于是就相当于执行了:
tail-n20error.log|grep--color-3hello
同理,可以把tail的行数和grep的上下文的行数也参数化:
#!/bin/bash
cd/usr/local/nginx/logs
tail-n$2error.log|grep--color-$3$1
这么一来,当执行下列命令时:
./search.shhello100010
就相当于:
tail-n1000error.log|grep--color-10hello
也即在日志文件的最后1000行里搜索,并显示关键行上下各10行的内容.
缺省值及判定逻辑
自然,好多时侯可能只想传递关键字即可,当把tail的行数和grep的上下文的行数也参数化后,每次调用也要传递它们是不便捷的,当假如把它们写死的话,有时我们可能又须要适当变化,这个矛盾如何解决呢?答案是借助缺省值和逻辑判定.
若果是常用的语言,如javagrep 命令参数,javascriptlinux下载工具,写个这些判定相信对你来说是个再简单不过的事red hat linux下载,对于bash这些脚本语言,最大的问题还是我们不熟悉其句型,这么这个还是跟之前说的那样,查查指南,或搜索下,过程就省略了,具体来说,可以这样:
#!/bin/bash
lineCount=1000
if[$2];then
lineCount=$2
fi
contextCount=10
if[$3];then
contextCount=$3
fi
cd/usr/local/nginx/logs
tail-n$lineCounterror.log|grep--color-$contextCount$1
简单说就是定义两个变量lineCount和contextCount,分别具有1000和10两个缺省值,之后借助if判定用户是否输入了第二和第三个参数,假如有,就用它们的值替代缺省值,没有的话就使用缺省值,这样一来就比较灵活了.
假如只输入了关键字:
./search.shhi
结果就是这样:
tail-n1000error.log|grep--color-10hi
输入两个参数:
./search.shhello300
结果就是这样:
tail-n300error.log|grep--color-10hello
输入三个参数:
./search.shhey5008
结果就是这样:
tail-n500error.log|grep--color-8hey
其实还是有个问题,当你想只调整第三个参数时,你还是必须得传入第二个参数,否则传入的值只会被第二个参数优先获得.
命令输出
最后,假如你想在执行前回显一下即将执行的命令,还可以借助echo这个命令来实现,它同样支持变量:
#!/bin/bash
lineCount=1000
if[$2];then
lineCount=$2
fi
contextCount=10
if[$3];then
contextCount=$3
fi
cd/usr/local/nginx/logs
echo”=========tail-n$lineCounterror.log|grep--color-$contextCount$1″
tail-n$lineCounterror.log|grep--color-$contextCount$1
这样一来,执行前都会先复印出即将执行的命令.
总结
综上所述,从单个命令到复合命令,再到脚本化和参数化,似乎是用了编程中的具象这一手法,这是我们解决重复性以及解决复杂性的一种重要手段.
当一个命令或几个的复合命令比较冗长时,我们就用一个脚本文件去做具象,保留不变的东西,把变化的东西参数化,外部化,通过这样的方法,就简化了执行(调用)的过程,降低了重复.
虽然,假如你常常须要查找日志的话,输入简单的./search.shfoo比反复输入这么之长的一串tail-n1000error.log|grep--color-10foo要便捷快捷的多.
作为一名程序员,降低重复是我们的天职,我们应当是怕重复,怕麻烦的,某种意义上,我们应当是”懒惰”的:
还记得Perl语言的发明人LarryWall的话吗:”优秀程序员应当有三大美德:懒惰、急躁和傲慢(laziness,impatienceandhubris)”
更多关于具象及重复的话题,可以参考之前计算机科学及重复性管理专题,关于使用tail结合grep查找日志关键字并高亮及显示所在行上下文就介绍到这儿.