ubuntu awk命令学习
1.awk命令学习
- awk脚本由模式和操纵组成
模式4种 | 操作主要4个 |
---|---|
/正则表达式/ | 变量或数组赋值 |
关系表达式 | 输出命令 |
模式匹配表达式 | 内置函数 |
BEGIN语句块、pattern语句块、END语句块 | 控制流语句 |
1.1脚本基本结构
awk ‘BEGIN{ print “strat” } pattern{commands} END{ print “end” }’ file
- 一个awk脚本通常包括:BEGIN语句块、能够使用模式匹配的通用语句块、END语句块3部分组成,这三个部分是可选的,任意一个部分不出现在脚本中,脚本通常是在单引号或双引号中,
例如:awk ‘BEGIN{ i=0 } { i++ } END{print i}’ file.txt
1.2awk的工作原理
- 第一步:先执行BEGIN{ commands}语句块中的语句;BEGIN语句块:在awk开始从输入流中读取行之前被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表头等语句;
- 第二步:从文件或标准输入(stdin)读取一行,然后执行pattern{ commands}语句块>,它将从第一行到最后一行扫描文件,指导整个文件被全部读取完毕;pattern语句块中的通用命令是最重要的部分,它也是可选的,如果没有提供pattern语句块,则默认执行{print},即打印每一个读取到的行,awk读取的每一行都会执行该语句块;
- 第三步:当读值输入流末尾时,执行END{ commands}语句块;在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果等,也是一个可选语句块;
1.3awk的例子
- 实例1
echo -e "A line 1\nA line 2" | awk 'BEGIN{print "start"} {print } END{print "End"}'
输出:
start
A line 1
A line 2
End
#echo -e表示shell开启转义字符即\n表示换行
- 实例2:当使用不带参数的print时,它就打印当前行; **当print的参数是以逗号进行分割时,打印时以空格作为定界符;
echo | awk '{var1="v1";var2="v2";var3="v3";print var1,var2,var3;}'
输出:
v1 v2 v3
- 实例3在awk的print语句块中双引号是被当做拼接符号使用的
echo | awk '{var1="v1";var2="v2";var3="v3";print var1"="var2"="var3;}'
输出:
v1=v2=v3
{}类似一个循环体,会对文件中的每一行进行迭代,通常变量初始化语句(如:i=0)以及打印文件头部的语句放入BEGIN语句模块;将打印的结果等语句放入END语句块中;
1.4 awk的内置变量(预定义的变量)
-
$n:当前记录的第n个字段,比如n为1表示第一个字段,n为2表示第二个字段;
-
$0:这个变量包含执行过程中当前行的文本内容
-
FILENAME:当前输入文件的名
-
FS:字段分割度(默认是任何空格)
-
NF:表示字段数,在执行过程中对应与当前的字段数
-
NR:表示记录数,在执行过程中对应与当前的行号
-
OFMT:数字的输出格式(默认是%.6g)
-
OFS:输出字段分隔符(默认是一个空格)
-
ORS:输出记录分隔符(默认是一个换行)
-
RS:记录分割符(默认是一个换行)
-
实例4
echo -e "line1 f2 f3\nline2 f4 f5\nline3 f6 f7" | awk '{print "Line No is:"NR,";NF:No of fields:"NF,";$0="$0,"$1="$1,"$2="$2,"$3="$3}'
#打印print以空格作为分割,输出结果以空格作为定界符
#print中""用作拼接,打印注释信息
#输出为:
Line No is:1 ;NF:No of fields:3 ;$0=line1 f2 f3 $1=line1 $2=f2 $3=f3
Line No is:2 ;NF:No of fields:3 ;$0=line2 f4 f5 $1=line2 $2=f4 $3=f5
Line No is:3 ;NF:No of fields:3 ;$0=line3 f6 f7 $1=line3 $2=f6 $3=f7
- 实例5
- 使用print N F < / k b d > 可 以 打 印 出 一 行 中 的 最 后 一 个 字 段 , 使 用 < k b d > NF</kbd>可以打印出一行中的最后一个字段,使用<kbd> NF</kbd>可以打印出一行中的最后一个字段,使用<kbd>(NF -1)则是打印导数第二个字段,其他以此类推:
echo -e "line1 f2 f3\nline2 f4 f5" | awk '{print $0"\n"$NF,$(NF-1)}'
#NF当前行的字段数,NF-1就少了1个
#输出:
line1 f2 f3
f3 f2
line2 f4 f5
f5 f4
- 实例6
#打印每一行的内容以及第二个和第三个字段并统计行数
echo -e "lien 1 is 1\nline 2 is 2\nline 3 is me" > log.txt ; awk '{print $0"\n"$2,$3} END{print NR}' log.txt
#NR:记录数,对应当前的行数
#上面END语句块,在读入每一行时,awk会将NR更新为对应的行号,达到最后一行NR就是最后一行的行号
- 实例7
一个每一行中第一个字段值累加的例子:
#seq 5产生1到5的所有整数, $1当前行的第一个字段内容,;表示该行语句结束类似c语言
#使用awk一定要注意格式是否正确,以及区分中英文
seq 5 | awk 'BEGIN{sum=0;print "总和:"} { print $1"+";sum+=$1} END{print "等于";print sum}'
- 实例7:将外部变量值传给awk:借助-v选项,可以将外部值(不是来自stdin标准输入的)传递给awk
VAR=1000
echo | awk -v VARIABLE=$VAR '{print VARIABLE}'
另一种传递变量的方式:变量直接使用空格分割符号作为awk的命令行参数,跟随在BEGIN、{}、和END语句块之后
var1="jhon"
var2="jhon2"
echo |awk '{print v1,v2}' v1=$var1 v2=$var2
输出:
jhon jhon2
当输入来自文件时使用:
awk '{print v1,v2}' v1=$var1 v2=$var2 log.txt
1.5awk的运算与判断
作为一种编程设计语言应具有的特点之一,awk支持多种运算,这些运算与c语言提供的基本相同。此外,awk还提供了一系列内置的运算函数(如log、sqr、cos、sin等)和一些用于对字符串进行操纵(运算)的函数(length、substr等等),awk允许多种测试;
- 算术运算符
运算符 | 描述 |
---|---|
+ - | 加减 |
* / | 乘除 |
& | 求余数 |
+ - ! | 一元加,减和逻辑非 |
^ *** | 求幂 |
++ – | 增加或减少 |
- 实例8:注意所有用作算术运算符进行操作,操作数自动转为数值,所有非数值都变为0
awk 'BEGIN{a="b";print a,a++,++a}'
输出:
b 0 2
- 赋值运算符:= += -= *= /= %= ^= **=
- 逻辑运算符:|| 逻辑或 && 逻辑与
- 实例9
awk 'BEGIN{a=1;b=2;print (a>5 && b<=2),(a>5 || b<=2);}'
输出:
0 1
- 正则运算符:~ ~!:匹配正则表达式和不匹配正则表达式
- 实例10:正则表达式:/正则表达式/
awk 'BEGIN{a="100testa";if(a ~ /^100*/){print "ok";}}'
输出:
ok
- 关系运算符:< <= > >= != == ,字符串比较按照ASCLL码顺序比较
awk 'BEGIN{a=11;if(a>=9){print "ok";}}'
#输出ok
- 其他运算符
运算符| 描述
:----: |:----:
$ | 字段引用
空格 | 字符串连接符
?加: |C条件表达式
in | 数值中是否存在某键值 - 实例11
awk 'BEGIN{a="b";print a="b"?"ok":"err"}'
#输出ok
awk 'BEGIN{a="b";arr[0]="b";arr[1]="c";print (a in arr);}'
#输出0
- 运算级优先级表:基本上和c语言的类似
1.6awk的高级输入输出
- next语句使用:在循环逐行匹配,如果遇到next,就会跳过当前行,直接忽视下面语句;而进行下一行匹配,net语句一般用于多行合并;
- 实例12
#当记录行号除以2余1时,就会跳过当前行,print NR,$0也不会执行,下一行开始,再次判断等于2,就会执行下面的语句块print NR,$0
seq 5 > log.txt
awk 'NR%2==1{next}{print NR,$0}' log.txt
#输出
2 b
4 d
- 实例13:分析发现需要将包含web行进行跳过,然后需要将内容和下面合并为一行:
cat text.txt
web01[192.168.2.100]
httpd ok
tomcat ok
sendmail ok
web02[192.168.2.101]
httpd ok
postfix ok
web03[192.168.2.102]
mysqld ok
httpd ok
0
awk '/^web/{T=$0;next;}{print T":t"$0;}' test.txt
#next满足正则表达式执行前{},不满足执行后{}
#输出:
web01[192.168.2.100]:httpd ok
web01[192.168.2.100]:tomcat ok
web01[192.168.2.100]:sendmail ok
web02[192.168.2.101]:httpd ok
web02[192.168.2.101]:postfix ok
web03[192.168.2.102]:mysqld ok
web03[192.168.2.102]:httpd ok
web03[192.168.2.102]:0
- 简单读取一条记录awk getline用法:输出重定向需要用到getline函数。
- getline从标准输入、管道或当前正在处理的文件之外的其他输入文件获得输入。它负责从输入获得下一行的内容,并给NF,NR和FNR等内建变量赋值。1.如果得到一条记录,getline函数返回1,如果到达文件的末尾就返回0;如果出现错误,例如打开文件失败就返回-1;2.当其左右有重定向符号 | 或 <时:getline作用于定向输入文件,由于该文件是刚打开,并没有被awk读入一行,只是getline读入,那么getline返回的是该文件的第一行,而不是隔行
- getline语法:getline var,变量var包含了特定行的内容
当其左右无重定向符 | 或 <是:getline作用于当前文件,读入当前文件的第一行给其后跟的变量var或$0;注意由于awk在处理getline之前已经读入了一行,所以getline得到的返回结果是隔行的
- 实例13
#执行linux的date命令,并通过管道输给getline,然后再把输出结果赋给自定义变量out,并打印它:
awk 'BEGIN{ "date" | getline out; print out }' test
#执行shell的date命令,并通过管道输给getline,然后getline从管道中读取并将它赋值给out,split函数把变量out转化为数组mon,然后打印数组mon的第二个元素:
awk 'BEGIN{"date" | getline out;split(out,mon);print mon[2]}' test
#命令ls的输出传递给getline作为输入,循环使getline从ls的输出中读取一行,并把它打印。这里没有输入文件,因为BEGIN块在打开输入文件前执行,所以可以忽略输入文件
awk 'BEGIN{while("ls" | getline) print}'
#关闭文件:awk中允许在程序中关闭一个输入或输出文件;
close("filename")
#filename可以是getline打开的文件也可是stdin,或包含文件名的变量或getline使用的确切命令;或一个输出文件,可以是stdout等
- 实例14:输出到一个文件:awk中允许用如下方式接结果输出到一个文件
#定向输出覆盖文件>
echo | awk '{printf{"hello world! jhon\n"} > "datafile"}'
#重定向输出末尾追加 >>
echo | awk '{print("hello world! jhon2\n") >> "datafile"}'
- 实例15:设置字段定界符:默认是空格,可以使用-F明确指定一个定界符
#分割符为: NF字段数:print $NF可以打印出一行中的最后一个字段
awk -F: '{print $NF}' /etc/passwd
#或在BEGIN语句块中使用OFS="定界符"设置输出字段的定界符
awk 'BEGIN{FS=":"}{print $NF}' /etc/passwd
1.7流程控制语句
在linux awk的while、do-while和for语句中允许使用break,continue语句来控制流程走向,也允许使用exit这样的语句退出,语法和c语言的类似,性能很快;
- 条件判断语句
- 实例16:条件判断语句:每条命令语句后面可以用;分号结尾
awk 'BEGIN{test=100;if(test>90){print "very good";} else if(test>60){print "good";}else{print "no pass";}}'
- 实例17 循环语句
#while循环语句 i默认为0
awk 'BEGIN{test=100;total=0;while(i<=test){total+=i;i++} print total;} '
#输出
5050
#for循环
#格式1
for(变量 in 数组)
{语句}
awk 'BEGIN{for(k in ENVIRON){print k"="ENVIRON[k]}}'
#ENVIRON环境变量关联数组
#格式2
for(变量;条件;表达式)
{语句}
awk 'BEGIN{total=0;for(i=0;i<=100;i++){total+=i;}print total;}'
#输出5050
#do 循环
do
{语句} while(条件)
awk 'BEGIN{total=0;i=0;do {total+=i;i++;} while(i<=100) print total;}'
exit语句使主输入循环退出并将控制转移到END,如果存在END的话,如果没有定义END规则,或END中应用exit语句,则终止脚本的执行;
1.8数组应用
- 数组是awk的灵魂,处理文本中最不能少的就是数组处理;因为数组索引(下标)可以是数字和字符串在awk中的数组叫做关联数组(associative arrays).awk中的数组不必提前声明,也不必声明大小,数组元素用0或空字符串来初始化,根据上下文而定
- 数组定义
Array[1]="sun"
Array[2]="kai"
#字符串做数组下标索引:
Array["first"]="jhon"
Array["last"]="jhon2"
#读取数组的值
#使用print Array[1]会打印sun,使用print Array[first]会打印jhon
{for(item in array){print array[item]};} #输出的顺序是随机的
{for(i=1;i<=len;i++){print array[i]};}
- 实例18:获取数组长度
awk 'BEGIN{info="it is a test";lens=split(info,tA," ");print length(tA),lens;}'
#length返回字符串以及数组长度,split进行分给字符串为数组,也会返回分割得到的数组长度
awk 'BEGIN{info="it is a test";split(info,tA," ");print asort(tA)}'
#asort对数组进行排序,返回数组长度
- 实例19:输出数组内容(有序无序输出)
#有序输出
awk 'BEGIN{info="it is a test"; tlen=split(info,tA," ");for(k=1;k<=tlen;k++){print k,tA[k];}}'
#注意数组下标是从1开始的,与c数组不一样
#无序输出
awk 'BEGIN{info="it not is a test"; split(info,tA," ");for(k in tA){print k,tA[k];}}'
#for....in输出,因为数组是关联数组,默认是无序的;
- 实例20:判断键值存在以及删除键值:
#错误的判断方法:
awk 'BEGIN{tB["a"]="a1";tB["b"]="b1";if(tB["c"]!="1"){print "no found";};for(k in tB){print k,tB[k];}}'
no found
a a1
b b1
c
#注意,没有定义tB["c"],但其值为空,因为awk数组是关联数组,只要通过数组引用它的key,就会自动创建该序列
#正确的判断方法
awk 'BEGIN{tB["a"]="a1";tB["b"]="b1";if("c" in tB){print "ok";};for(k in tB){print k,tB[k];}}'
a a1
b b1
#删除键值,通过delete array["key"]
awk 'BEGIN{tB["a"]="a1";tB["b"]="b1";delete tB["a"];for(k in tB){print k,tB[k];}}'
#输出 b b1
- 二维数组、多维数组的使用
本质上和一维数组是一样的,awk在存储上并不支持多维数组。awk提供了逻辑上模拟二维数组的方位方式;例如array[2,4]=1是访问允许的。awk使用一个特殊的字符串SUBSEP(�34)作为分割字段,在上面的例子中,关联数组array存储的键值实际上是2�344。 - 多维数字可以使用if((i,j) in array)这样的语法,但是下标必须放在圆括号里面;以及使用for(item in array)遍历数组,与一维数组不同的是,多二维数组必须使用split()函数来访问单独的下标分量。
awk 'BEGIN{for(i=1;i<9;i++){for(j=1;j<=9;j++){tarr[i,j]=i*j;print i,"*",j,"=",tarr[i,j];}}}'
#可以通过array[k,k2]引用来获得数组内容
#另一种方法
awk 'BEGIN{for(i=1;i<9;i++){for(j=1;j<=9;j++){tarr[i,j]=i*j;}}for(m in tarr){split(m,tarr2,SUBSEP);print tarr2[1],"*",tarr2[2],"=",tarr[m];}}'
#多维数组必须使用split()函数来访问单独的下标分量
1.9内置函数以及其他函数:遇到在查找
未完待续
awk参考