Ubuntu下awk详细学习

ubuntu awk命令学习

  • 1.awk命令学习
    • 1.1脚本基本结构
    • 1.2awk的工作原理
    • 1.3awk的例子
    • 1.4 awk的内置变量(预定义的变量)
    • 1.5awk的运算与判断
    • 1.6awk的高级输入输出
    • 1.7流程控制语句
    • 1.8数组应用
    • 1.9内置函数以及其他函数:遇到在查找

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参考

你可能感兴趣的:(shell编程)