在很多面试中,只要求职者写了熟悉linux的时候,基本所有面试官都会问的一个问题就是
服务器上有一个很大的文件,如何从里面找到某个关键字?
首先我们不管是作为一个测试还是开发人员,分析问题一点要从多个方面入手,并且如果可能的话先别急着回答,先想并提问题,敢于质疑,才是一个好测试:
- 需要找到所有匹配的关键字还是只需要找到第一个匹配的关键字?
- 只找某个关键字就可以了吗?需要关键字的前后几行来协助分析吗?
- 关键字的匹配是否需要考虑大小写?
- 是否需要在匹配结果中同时显示关键字所在的行号?
- 这个大文件高于服务器内存了吗?
解决方案
1.关键字的匹配是否需要考虑大小写?
#若不需要考虑大小写,可以grep -i
grep -i "关键字" /path/filename
第一个是一个最基础的回答,但是记住,grep只会返回符合条件的行,并不会返回上下文信息
2.需要找到所有匹配的关键字还是只需要找到第一个匹配的关键字?
#对于匹配几个关键字,可以通过grep -m来实现,-m 后面的数字为我们要返回的关键字数
grep -m 10000 "关键字" /path/filename
#结合一下一二点就是,既不区分大小写,又仅返回前1024个
grep -i -m 1024 "关键字" /path/filename
3.是否需要在匹配结果中同时显示关键字所在的行号?
#显示关键字所在的行号,可以grep -n处理
grep -n "关键字" /path/filename
4.只找某个关键字就可以了吗?需要关键字的前后几行来协助分析吗?
#需要前后几行的话又很多种方法
1.通过grep -C(大写)后面跟数字,来搜索关键字的上下num行
grep -C5 "关键字" /path/filename #搜索关键字的上下5行
2.通过grep命令后加more实现
grep "关键字" /path/filename | more #more仅可向后翻页
3.通过grep命令后加less实现
grep "关键字" /path/filename | less #less支持向前向后翻页
4.使用awk命令,其中 NR 表示当前行号,$0 表示整行内容,这条命令会把包含关键字的行及其行号打印出来。
awk '/关键字/{print NR" "$0} ' /path/filename
5.使用more或者less
如less ./logs,进入页面后再:后面输入/+需要搜索等关键字,这样就可以了
5.文件过大怎么办?
如果文件过大,在搜索关键字时可能会面临内存不足的问题,我们的解决方案可以为:
1.将文件划分为多个小文件,逐个文件进行搜索。这种方式需要考虑如何划分文件,以及如何对搜索结果进行合并。实现过程如下:
#以下命令将把/path/filename划分为1000MB大小的文件,并且将分割后的文件存储到/path/to/outputdir目录中,文件名以prefix开头(如果不指定前缀,默认以x开头)
- split -b 1000M /path/filename /path/to/outputdir/prefix
#以下命令将搜索/path/to/outputdir/目录中所有文件中包含"关键字"的行,并将结果保存到/path/to/resultfilename文件中。
- grep "关键字" /path/to/outputdir/* > /path/to/resultfile
2.使用流式处理技术,在读取文件时只保留必要的数据,而不是将整个文件读取到内存中。例如,使用管道操作符将多个命令连接起来,构成一个流处理管道。
#将一个大文件按行读取,并找到包含指定关键字的行,并输出该行及其前5行和后5行:
grep -n "关键字" /path/failname| awk -F ':' '{print $1}' | sed -e 's/$/ -5p/' -e 's/^/\'':/+1' /path/failname | sh
这个命令的流程是:
1.使用 grep -n "关键字" /path/failname 命令在文件 /path/failname 中搜索关键字,并显示关键字所在的行号。其中 -n 选项表示显示行号。
2.使用管道将上一步的输出传递给 awk -F ':' '{print $1}' 命令。这个命令的作用是将每一行的输出只保留行号部分,并去掉冒号和关键字的部分。
3.使用管道将上一步的输出传递给 sed 命令。这个命令的作用是对每一个行号进行处理,使得能够显示关键字所在行前面的 5 行内容和后面的内容。具体来说,命令 -e 's/$/ -5p/' 将每一个行号结尾添加 -5p,表示显示该行前面的 5 行内容;命令 -e 's/^/\'':/+1' 将每一个行号开头添加 :' +1,表示显示该行后面的内容。
4.最后使用管道将上一步的输出传递给 sh 命令,执行显示操作。
以上命令使用了流式处理技术,不会将整个大文件读取到内存中,只保留必要的数据逐行处理。