注意:本文并非一篇awk入门文章,而是偏重实例讲解html
awk借鉴了c语法,所以awk在许多地方还保留有c语言的痕迹,好比printf语句;for,if的语法结构等linux
最简单地说,AWK 是一种用于处理文本的编程语言工具,处理模式是只要在输入数据中有模式匹配,就执行一系列指令。awk命令格式为:正则表达式
awk {pattern + action} {filenames}
相比于sed经常做用于一整行的处理,awk则比较倾向于将一行分红数个字段来处理。awk将输入数据视为一个文本数据库,像数据库同样,它也有记录和字段的概念。默认状况下,记录的分隔符是回车,字段的分隔符是空白符(空格,\t),因此输入数据的每一行表示一个记录,而每一行中的内容被空白分隔成多个字段。利用字段和记录,awk能够很是灵活地处理文件shell
一个典型的awk语法以下:数据库
awk '{ BEGIN{stat1} BEGIN{stat2}
pattern1{action1} pattern2{action2} ... patternn{actionn} {默认动做,无条件,始终执行} END{stat1} END{stat2} }'
pattern,部分和随后的if,for部分,能用到的符号有:编程
$0 当前记录(这个变量中存放着整个行的内容) $1~$n 当前记录的第n个字段,字段间由FS分隔 FS 输入字段分隔符 默认是空格或\t NF 当前记录中的字段个数,就是有多少列 NR 已经读出的记录数,就是行号,从1开始,若是有多个文件话,这个值也是不断累加中。 FNR 当前记录数,与NR不一样的是,这个值会是各个文件本身的行号 RS 输入的记录分隔符, 默认为换行符 OFS 输出字段分隔符, 默认也是空格 ORS 输出的记录分隔符,默认为换行符 FILENAME 当前输入文件的名字
#在任什么时候候{}内均可以跟多个并列动做(使用“;”分隔),下面的{action1} 和 {action1;action2;...} 都表示{}体内有多个动做,两种表示没有任何区别,写第二种仅仅是为了直观的表示能够有多个动做数组
#for循环写法编程语言
for(i=1;i<=NF;i++){action1; action2; ..} #{}中用分号分隔多个动做 for(i=1;i<=NF;i++)if; else if;else #for后接一个if结构 for(i=1;i<=NF;i++)printf “for add” #简单的循环打印
#if 判断写法函数
if($1 ~ /reg/){action1}; else if($1 ~ /reg2/){action2}; else{action3} #else if部分能够没有 if($1 ~ /reg/ && $2 ~ /reg2/){action} #多个条件用”&&”,”||”表示 if($1 ~ /reg/ || NR >= 5){action
# if,for 混合写法工具
{ for(i=1;i<=NF;i++)if(…) printf “test”; else if(…) printf “test2”; else printf “test3”; print "not_for" }
#print “not_for”部分是并列与for循环结构的另外一个action,在for循环以外,只会打印一次 { for(i=1;i<=NF;i++){if(…) printf “test”; else if(…) printf “test2”; else printf “test3”;print “in_for“}; print "not_in_for" } { for(i=1;i<=NF;i++){if {s1;s2;} else if {s3;s4;} else {s5;s6;}; print "test"} } #else if前不加分号 { for(i=1;i<=NF;i++)printf "for_add"; if(…);else if(…); else } #if并不在for循环体内
if语句的做用范围:
1: AWK使用的RE为ERE
2: 若是在BEGIN中设置了OFS, 只有$0有改动OFS才能生效
3: printf 与 print 的区别: printf 不自动打印换行符, print 则自动打印
4: gsub的返回值并非替换后的字符串,而是返回替换的次数
5: 字符串常量必定在用" "包围起来,不然看成变量使用, 如 $1=="ipaddress"
6: AWK 的 for 循环为 C-Style,即为 for(), 区别于shell中的for i in ...
7: AWK中可使用多个分隔符,要封装在方括号里,用' '包围,以防 shell 对它们进行解释,如 awk -F '[ :/t]' ,使用空格,冒号,tab做为分隔符
8: next语句:从输入文件中取得下一个输入行,在AWK命令表顶部从新执行命令,通常用于跳过一些特殊的行
9: awk 匹配多个条件: awk '/kobe/ && /james/' #匹配同时有kobe和james的行
10: FS的默认值是[ /t/n]+, OFS的默认值为空格,RS,ORS的默认值都是换行
11: 定位行有两种方法: 1: NR==行号 2: 用RE /Love$/
12: exit语句:终止AWK程序,但不跳过END语句
13:$1..$n表示第几列(字段),$0表示整个行.
14:awk可用比较运算符:!=, >, <, >=, <=
15: 字符串匹配:~: 匹配 !~: 不匹配
16: &&:多个条件且, || 多个条件或
17: {s1;s2;s3;...}中多个语句用分号隔开;if; else if; else
18: print 后不带任何参数时,至关于print $0 ,将会打印整行记录
函数 | 说明 |
gsub( Ere, Repl, [ In ] ) | 除了正则表达式全部具体值被替代这点,它和 sub 函数彻底同样地执行,。 |
sub( Ere, Repl, [ In ] ) | 用 Repl 参数指定的字符串替换 In 参数指定的字符串中的由 Ere 参数指定的扩展正则表达式的第一个具体值。sub 函数返回替换的数量。出如今 Repl 参数指定的字符串中的 &(和符号)由 In 参数指定的与 Ere 参数的指定的扩展正则表达式匹配的字符串替换。若是未指定 In 参数,缺省值是整个记录($0 记录变量)。 |
index( String1, String2 ) | 在由 String1 参数指定的字符串(其中有出现 String2 指定的参数)中,返回位置,从 1 开始编号。若是 String2 参数不在 String1 参数中出现,则返回 0(零)。 |
length [(String)] | 返回 String 参数指定的字符串的长度(字符形式)。若是未给出 String 参数,则返回整个记录的长度($0 记录变量)。 |
blength [(String)] | 返回 String 参数指定的字符串的长度(以字节为单位)。若是未给出 String 参数,则返回整个记录的长度($0 记录变量)。 |
substr( String, M, [ N ] ) | 返回具备 N 参数指定的字符数量子串。子串从 String 参数指定的字符串取得,其字符以 M 参数指定的位置开始。M 参数指定为将 String 参数中的第一个字符做为编号 1。若是未指定 N 参数,则子串的长度将是 M 参数指定的位置到 String 参数的末尾 的长度。 |
match( String, Ere ) | 在 String 参数指定的字符串(Ere 参数指定的扩展正则表达式出如今其中)中返回位置(字符形式),从 1 开始编号,或若是 Ere 参数不出现,则返回 0(零)。RSTART 特殊变量设置为返回值。RLENGTH 特殊变量设置为匹配的字符串的长度,或若是未找到任何匹配,则设置为 -1(负一)。 |
split( String, A, [Ere] ) | 将 String 参数指定的参数分割为数组元素 A[1], A[2], . . ., A[n],并返回 n 变量的值。此分隔能够经过 Ere 参数指定的扩展正则表达式进行,或用当前字段分隔符(FS 特殊变量)来进行(若是没有给出 Ere 参数)。除非上下文指明特定的元素还应具备一个数字值,不然 A 数组中的元素用字符串值来建立。 |
tolower( String ) | 返回 String 参数指定的字符串,字符串中每一个大写字符将更改成小写。大写和小写的映射由当前语言环境的 LC_CTYPE 范畴定义。 |
toupper( String ) | 返回 String 参数指定的字符串,字符串中每一个小写字符将更改成大写。大写和小写的映射由当前语言环境的 LC_CTYPE 范畴定义。 |
sprintf(Format, Expr, Expr, . . . ) | 根据 Format 参数指定的 printf 子例程格式字符串来格式化 Expr 参数指定的表达式并返回最后生成的字符串。 |
awk '/AL/ {printf $1; print $2}' emp.txt awk '/AL/{print $1} {print $2}' emp.txt
1.第一种只处理匹配到AL的行; 而后打印这些行的第一字段和第二字段
2.第二种只有在匹配到AL的行才打印字段一,可是字段二是无条件的,始终打印
以下文本test.log,须要把文本中单位是"M"的字段转换为"G"
16G 16G 1.9G 40G none 4G 4G 952M 60G 16G 16G 1.6G 40G none 5G 780M 5G 80G
若咱们单纯地想替换字符串,则可使用一下命令,注意:print没带默认参数时,默认打印整行记录
cat 1.txt | awk '{
sub(/M/,"G",$i)
}'
完整替换以下,替换的同时进行数值计算
cat 1.log | \ awk '{ for(i=1;i<=NF;i++){ if($i ~ /M/) printf int(substr($i,1,match($i,/M/)-1)/1024*100)/100"G\t" else printf $i"\t" } print "" }'
1.使用for遍历每一行的每个字段,使用if处理匹配到"M"的字段,而后调用awk内置函数int, substr, match处理, 关于awk内置函数,能够参考linux awk 内置函数详细介绍(实例)
2.先乘100,再除以100,是为了转换为"G"单位后保留两位小数
3.注意 printf,print 两个函数的区别
如第二个例子中的文本,如今须要分别统计每行中带有"G", "M", "none" 字段的个数,并输出
cat test.log | \ awk ' BEGIN{OFS="\t"} { a=b=c=0 for(i=1;i<=NF;i++){ if($i ~ /G/) a+=1 else if($i ~ /M/) b+=1 else c+=1 } print $0,"G:"a,"M:"b,"none:"c } END{print "end!"} '
结果以下:
16G 16G 1.9G 40G G:4 M:0 none:0 4G 4G 952M 60G G:3 M:1 none:0 16G 16G 1.6G 40G G:4 M:0 none:0 5G 780M 5G 80G G:3 M:1 none:0 end!
给出一个以下文本test2.log,在每一行中,只输出字母及其以前的字符
1 2 a 5 6 11 b 55 66 21 22 23 c 25 26
写法以下:
cat test2.log | \ awk '{ for(i=1;i<=NF;i++){ if($i ~ /[a-z]/) { printf $i"\t" break } else printf $i"\t" } print "" }'
1.break 用法跟c语言用法同样,跳出for循环
以下文本test3.log,给定一个id值,输出其在全部id中是第几个
test1
id=615187629 test2
test3
id=615183434 test4
id=615123789 test5
id=615975882
给定id值615123789,其在全部id中是第三个,计算以下:
cat test3.log | \ awk -v var1=615123789 -F [=] ' /id/ { b+=1 a[b]=$2 } END { for(i in a) if(a[i] == var1) print "number:",i } '
6.awk中的整数计算
以下文本,这是一个kvm虚拟机进程(省略了部分文本),咱们要获取其映射到宿主机上的vnc端口号,即由"-vnc 0.0.0.0:1"字符串计算出其vnc端口号为5901(5900 + 1),如果"-vnc 0.0.0.0:2",则端口号为5902
cat kvm.txt
qemu 144148 4.7 4.2 ... /usr/local/qemu/bin/qemu-kvm -name lnmptest-107 ... -device isa-serial,chardev=charserial0,id=serial0 -vnc 0.0.0.0:1 -vga cirrus timestamp=on
实现有多种,sed,shell,awk均可以
#awk
cat kvm.txt | awk '{ for(i=1;i<=NF;i++){if($i == "-vnc"){sub(/0.0.0.0:/,"",$(i+1));print $(i+1)+5900}} }'#shellcat kvm.txt | egrep -o "\-vnc [^ ]*" | awk -F: '{print $2+5900}'#sedcat kvm.txt | sed "s/^.* -vnc [0-9.]*:\([0-9]*\).*/\1+5900/g" | bc