三剑客grep,sed,awk

2019-05-05  本文已影响0人  HugiFish

log分析是非常常见的异常追查手段,但是由于生产环境下log体量极为庞大,正确的选择分析工具是快速排查异常的先决条件。
那么如何从海量的log信息中基于特定的格式自动抽取我们需要的信息呢?这里我们就会用到Linux世界里赫赫有名的三剑客(grep,sed,awk)


1.概述

grep:是Linux系统中的一个文本搜索工具,使用正则表达式搜索文本,功能最为简单,也最快,适用于单纯的查找和匹配
sed:是一种非交互式编辑器,能够执行vi相同的编辑任务,一次处理一行内容,适用于编辑匹配文本
awk:是一个强大的文本分析工具,也可以把他理解为脚本语言解释器,有很多内建功能,适用于处理格式化的文本,对文本进行复杂的格式化处理
综上所述:
应用难易程度:grep <= sed < awk
功能:grep < sed <awk
下面由易到难逐步介绍这几个命令:


2.grep

输入:文件或一个标准输入stdin或管道
输出:打印至屏幕,或重定向至文件,或输出至管道
命令格式: grep [options] 'pattern{actions}' filename
grep单独使用时grep会一直等待,直到该程序被中断。如果遇到了这样的情况,可以按“Ctrl+c”终止。避免这种情况的方式是将需要查询的文件通过管道扔给grep程序,例cat /etc/passwd | grep root

2.1常用的option ()内是笔者记忆的窍门

-o 这个选项时最常用,只显示匹配内容,grep默认显示满足匹配条件的那一行的所有结果 (only-matching)
-i 比较字符时忽略大小写(ignore)
-w 模式作为单词来查找,相当于正则中"<word>" (word)
-x 只显示被匹配到的内容是整行的结果,相当于正则中"^$",一般是用于利用正则表达式匹配整行(regexp)
-v 取反,输出我们定义模式相反的内容 (invert)
-c 统计匹配到的行数,统计匹配比例时可以,比如先计算出匹配行数,然后用wc -l 计算出总行数,这样就可以求出匹配比例(count)
-m 可以匹配的最大个数 ,比如特例抽取时,可以通过限定匹配的最大个数,简化结果(max)
-n 在输出结果时显示行号 (number)

2.2常用的正则表达式

^行首
$行尾
\<词首
\>词尾
. 除换行符以外的所有字符
*匹配零个或者多个前导字符
[]匹配任意一个包含字符
[^]匹配不属于包含字符中的任意一个字符
x{m}重复字符x,m次,如grep 'a{5}'表示匹配包含5个a的行
x{m,}重复字符x至少m次,
x{m,n}重复字符x至少m次,但不能多于n次如grep 'a{5,10}表示匹配包含5~10个a的行


3.sed

输入:文件或者一个标准输入或管道
输出:打印至屏幕,或重定向至文件,或输出至管道
工作原理:sed会逐行从数据源读入数据,放置在一个临时缓冲区(术语叫模式空间)中,然后对数据进行处理后输出,默认理论上是不修改数据源的内容,但是如果加入-i操作,就会将处理后的数据输出至数据源,所以需要-i操作时一定要慎重。此外,如果是连续命令,sed每个命令都是前一个命令的输出,所以命令顺序是很重要的,例如:sed 's/abc/def/g;s/def/jqk/g' file,这个命令相当于sed 's/abc/jqk/g' file,因为file文件中的abc 先被替换成def,然后def再被替换成jqk。
命令格式:sed [options] 'pattern{actions}' filename

3.1常用的option ()内是笔者记忆的窍门

-r sed 使用扩展正则。
-i 直接修改文档读取内容,不再屏幕上输出。
-n 使用安静模式,只打印被sed处理的行。⚠️此选项只有当行整体内容被改变时才会打印处理的行,比如a,i,c命令;如果后面内容处理命令是s部分替换命令,需要配合p命令才可以打印出处理的行,例如 sed -n 's/root/newroot/gp' /etc/passwd

3.2常用处理命令

处理命令中不仅仅包含编辑命令,还有地址匹配命令。地址匹配命令是处理命令中非常重要的一个部分,它的作用是告诉sed处理命令适用于那些行,缺省的地址匹配命令是全局匹配,即对所有内容执行编辑操作。
地址匹配命令:行号(例如:^,1,2,3,4....$ )或正则表达式 缺省值为全局匹配,即编辑命令应用到所有行 指定了一个值,表示编辑命令只匹配该行,这个值可以由行号给出,也可以由正则表达式匹配后给出 指定一个地址对,将编辑命令应用至这个地址对包含范围中的所有行,地址对可以全部由行号组成,也可以全部由正则表达式组成,还可以由行号和正则表达式混合组成,例如:1.(2,5)表示2到5行;2.(/正则1/,/正则2/) 正则1为开始条件,正则2为结束条件,先找满足开始条件的行,然后再找满足结束条件的行,如果找到了满足结束条件的行后,再向下继续寻找满足开始条件的行。3.(/正则1/,3)或者(2,/正则2/) 反向地址匹配,在地址后边添加一个感叹号(!),则将编辑命令全部应用到没有匹配到的文本行上
a:在满足条件的行的下一行添加(add)
i: 在满足条件的行前插入文本⚠️(与选项中的-i要区分开)(insert)
d:把满足条件的行全部删除(delete)
c:用新文本替换匹配范围内的所有行⚠️(是把所有行全部替换新文本,而不是逐行替换)(copy)
s:用新文本替换匹配上的字符串(部分替换)(seperate)
g: 对每行进行执行全局匹配(global)
⚠️s一般和g一起使用,如果一行出现多个正则匹配项,单独使用s命令,只会替换每一行第一个匹配项,如果在命令末端添加g,则可以替换每一行全部的匹配项



y: 转换命令,字符按照一对一的方式从左到右进行转换不能对正则表达式使用此命令,例如:y/abc/ABC/,会将a转换成A,b转换成B,c转换成C。(Transliterate转译,可以利用译的拼音yi的首字母)
p: 打印命令,用于显示模式缓存区的内容。(print)
q: 结束命令,处理到匹配行后sed程序不再执行(quit)
示例
#匹配root行 与匹配bin行之间所有内容,在每一行的后面追加一行test字符串
sed '/root/,/bin/a test' /etc/passwd
#第一行和第一个匹配bin行之间的所有内容,在每一行的前面插入一行test字符串 
sed '1,/bin/i test' /etc/passwd 
#删除第一行至第五行的所有内容
sed '1,5d ' /etc/passwd
#将匹配root行到第5行的所有内容替换成 test
sed '/root/,5c test' /etc/passwd
#将所有内容中 root替换成newroot
sed 's/root/newroot/g' /etc/passwd
#只打印执行了上边处理命令的行
#⚠️如果sed采用安静模式时sed -n,后序的处理命令是替换命令s时,需要配合p命令使用才可以打印出处理过的行,否则将无任何输出
sed -n 's/root/newroot/gp' /etc/passwd

3.3多行处理命令(进阶)

上面我们提到了,sed命令是将每一行的内容读到一个叫模式空间的缓冲区里,然后执行命令。并且如果sed执行N/D/P 三个命令,还会形成多行处理的模式空间,但是多行处理时某些场景可能需要保存模式空间中被处理的内容,这里就引入了另外一个缓存空间--保持空间。
保持空间用于保存模式空间的内容,两个空间中的内容可以相互复制

命令 说明 记忆
h/H 将模式空间的内容复制或者追加到保持空间 hold
g/G 将保持空间的内容复制或者追加到模式空间 get
x 交换两个空间的内容 exchange

小写是覆盖目的空间的内容,大写是追加到目的空间,追加的内容前加\n区别于原有内容
经典例子:
如何利用sed替换换行符?

sed 'H;$!d;${x;s/^\n//;s/\n/,/g}' /etc/passwd

解析:H将每一行追加到保持空间,$!d对不是最末一行的都执行删除操作,主要是利用d命令打断常规的命令执行流程,让sed继续读入新的一行,直接到最后一行都放在保持空间,x将保护空间与模式空间模式对调,s/^\n//去掉空行,s/\n/,/g将换行符替换成逗号

3.4流程控制(进阶)

sed提供了分支命令b(branch)和测试命令test两个命令来控制流程,可以实现类似与C++中goto语法的想过,跳转到指定标签位置继续执行命令。标签是以符号 : 为开头

命令 说明 区别
b label 跳转到分支label处,如果label不存在则索引到命令尾部 在匹配行进行无条件跳转
t label 在最后读取完最后一行输入或者上一次执行t命令之后,成功执行了一次s///语句,分支到label,如果label不存在则索引到命令尾部 需要成功执行一次s替换语句才可跳转,即有条件跳转

用法如下:

  1. 基本用法
':lable;command1;command2;/pattern/b lable;command3'

执行到pattern/b lable时,如果匹配,则跳转到头,继续执行command1,如果不匹配,则往下执行command3

  1. 仿if流程控制
'command1;/pattern/b if;commad2;b;:if;command3'

执行/pattern/b if,如果匹配则执行commad3,如果不匹配则执行command2,然后跳到最后(分支命令如果后边不接标签,则默认位置为最后)

  1. 只利用模式空间将换行符替换成","方法有很多中,命令也有很多种
#利用test标签
sed ':label;$!N;s/\n/,/g;t label' /etc/passwd
#利用branch标签
sed ':label;$!N;s/\n/,/g;$!b label' /etc/passwd
#利用y转换命令,由于只转换一个字符
sed ':label;$!N;y/\n/,/;$!b label' /etc/passwd

4.awk

4.1awk文本处理流程

首先,我们需要了解awk命令处理文本的流程,awk从输入中读入数据时,工具RS会讲输入分割为许多记录(records),awk的每一次循环都会读取一个record进行处理。
awk默认的记录分隔符(RS)为换行符,因此缺省记录分隔符,awk一次处理一行。
在处理每一行时,awk会将根据字符分隔符(FS)分割读入的记录文本,分割成列。
awk默认的字段分隔符是空格,所以,awk默认会根据空格或者连续几个空格划分每一个记录。

abc def   hidk
|1 | 2  |  3   |
|       0      |
$0代表整条记录,$1表示第一列,$2表示第二列

命令格式:awk [options] 'pattern {actions}' filename

4.2常用option

-F:字段分隔符,⚠️大写(field-separate)
-v:设置变量(var)可以设置内置变量,也可以设置自定义变量

awk -F":" '{print $1}' /etc/passwd
awk -v FS=":" '{print $1}' /etc/passwd

上段代码是自定义字段分隔符的两种模式,一个是利用的-F直接设置字段分隔符,一个利用的是-v设置awk内置变量,字段分隔符的内置变量名是FS。
下面列举一下awk常用的内置变量:

内置变量名 说明
RS 输入记录分隔符的值,缺省值为\n
ORS 输出记录分隔符的值,缺省值为\n
FS 输入字段分隔符的值,缺省值为空格
OFS 输出字段分隔符的值,缺省值为空格
FNR 每个文件已经处理的记录数,多文本处理时,切换文件时,此值都会重置
NR 已经处理的累计记录书,多文本处理时,切换文件,此值依然不会重置
NF 当前行的字段的个数
FILENAME 当前处理的文件名
ARGC 表示命令行中除了选项和选项参数以外的所有参数的个数,awk命令也算一个参数,表示命令名位置是ARGV[0]
ARGV 数组,记录命令行中的参数,数组下标从0开始
awk通过-v对多个内置变量进行设置

4.3常用命令

4.3.1BEGIN和END

命令是pattern+actions的合集,pattern可以是正则表达式,也可以是布尔表达式,还可以是两个特殊的模式:BEGIN和END

4.3.2pattern

除了BEGIN和END两个特殊匹配模式,还有正则表达式/REG/actions,和布尔表达式/bool /actions。

awk '/root/{print $0}' /etc/passwd#正则表达式
awk 'NR==3{print $0}' /etc/passwd #布尔表达式

4.3.3命令中的actions

awk是类似与shell的脚本编程语言,既然是编程语言,就必须支持最基本的变量定义以及必要的流程控制。

4.3.3.1独立运行的脚本

  1. awk 有一个叫做-f的选项注意和-F加以区分 ,这个选项可以接awk源码文件,在Linux及UNIX中凡是#!后边跟的统统是加载器(解释器)的路径,所以我们可以通过这一特性创建属于我们自己的awk文件,利用which确定自己的awk路径,例如/bin/awk
#!/bin/awk -f
BEGIN {print "i'm awk file"}

命令为test.awk,然后通过chmod 命令添加可执行权限,然后运行./test.awk


注意:awk文件中是多个'pattern{actions}'组成,不能在文件中指定处理参数options,也不能指定处理文件,处理文件在文件命令外部执行,例如 ./test.awk /etc/passwd
  1. 通过shell脚本调用
#!/bin/bash
awk -F ":" '{
#actions
}
' /etc/passwd

实际上就是将awk命令分行编写不需要加";"进行命令分割,有利于阅读

4.3.3.2变量

#!/bin/awk -f
BEGIN {
list[0]="abc"
list[1]="def"
list[2]="xyz"
print list[0]
print list[1]
print list[2]
}

4.3.3.3流程控制

如果你有C语言基础,阅读awk命令毫无障碍

#!/bin/awk -f
BEGIN {
list[0]="abc"
list[1]="def"
list[2]="xyz"
if( "def" == list[0] ){
    print "index 0 "list[0]
}
else if("def" == list[1]){
    print "index 1"list[1]
}
else{
    print"index nothing"
}
}
#!/bin/awk -f
BEGIN {
list[0]="abc"
list[1]="def"
list[2]="xyz"
count=0
do{
    print list[count]
    count++
}while(count<3)
}
#!/bin/awk -f
BEGIN {
list[0]="abc"
list[1]="def"
list[2]="xyz"
for(i=0;i<3;i++){
   print list[i]
}
for( i in list){
   print list[i]
}
}

4.3.3.4函数(进阶)

拥有函数语法是awk被称为语言的重要原因之一

1.自定义函数

格式为:function name(arg1,arg2,...){statements}
参数列表由逗号分割,参数默认为局部变量,无法在函数之外访问,但是⚠️⚠️在函数内部定义的变量是全局变量,是可以在函数之外访问

#!/bin/awk -f
function fun(a){
  b=a
  print "a in function = "a
}
BEGIN {
  print "b before func = "b
  fun("test")
  print  "a = "a
  print  "b = "b
}

2.库函数

1.数学函数
函数名 说明
srand(n) 设置随机种子,默认为当前时间为种子
rand() 返回[0,1)中的一个随机值,注意不包含1
int(n) 强制转化成整数,当不是数字时,返回数值为零
sqrt(n) 绝对值函数
sin(n) 正弦
cos(n) 余弦
exp(n) e的n次方的指数
log(n) e的n次对数
2.字符串函数
函数名 说明
sub(pattern,repl[,in]) 将in(输入)中的pattern内容替换成repl,只替换第一个匹配的substring
gsub(pattern,repl[,int]) 和sub功能类似,但是是全局替换
length([,in]) 字符串长度,如果没有指定,默认使用$0
index(s,t) 字符串t在s第一次出现的位置,位置从1开始计算,如果没找到返回0
match(s,pattern[,array]) 找到s中pattern匹配的起始位置,并返回,位置从1开始计算,不匹配返回0,函数定义了两个全局的内置变量RSTART和RLENGTH,RSTART与返回值相同,RLENGTH记录匹配子串的长度,如果没有找到,此值为-11.没有array的情况下,通过pattern匹配,在s中寻找最左边,最长substring,返回的就是substring的index;2.存在array的情况下,将pattern中用()匹配的内容按照顺序放入array中,array[1]代表第一个()中的内容
split(s,array[,fs]) 字符串根据fs切分s字符串然后存入array中,fs的缺省值为内置变量FS的值
strsub(s,m[,n]) 返回从位置m开始,长度为n的字串,位置从1开始
tolower(s) 全部变成小写
toupper(s) 全部变成大写
spritf(format,arg,...) 格式化输出
sub与gsub直观区别
3.时间函数
命令名称 说明
mktime(datespec) 参数按照(YYYY" "MM" "DD" "HH" "MM" "SS)注意空格分割,空格需要双引号包围
strftime([format [, timestamp[, utc-flag]]]) 将时间戳格式化后自定义输出,utc缺省值为本地时间,格式化模式字符与ANSI C的strftime一致
systime() 返回当前的时间戳
上一篇下一篇

猜你喜欢

热点阅读