awk基本应用

一、何为awk

awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk明显复杂的多也强大的多。如果虽然awk可以实现grep和sed的一些功能,但是效率却比grep和sed慢。故如非是在show,否则建议不要使用awk去实现grep或sed的功能。在linux中awk其实是gawk,gawk是免费的!这才是重点。

二、awk语法

man下awk文档,你会发现

NAME
       gawk - pattern scanning and processing language

SYNOPSIS
       gawk [ POSIX or GNU style options ] -f program-file [ -- ] file ...
       gawk [ POSIX or GNU style options ] [ -- ] program-text file ...

       pgawk [ POSIX or GNU style options ] -f program-file [ -- ] file ...
       pgawk [ POSIX or GNU style options ] [ -- ] program-text file ...

对于manual中的内容,可能会比较难理解,故简化为如下格式

	awk [options] 'program' file file ...
	awk [options] 'PATTERN{action}' file file ..

常用options

-F fs                     使用‘fs’作为文本分隔符

–field-separator fs

[leon@vm tmp]$ awk -F: '/^root/{print $1,$7}' /etc/passwd
root /bin/bash

-v var=val   定义一个变量

–assign var=val

[leon@vm tmp]$ awk -v name=leon 'BEGIN{print name}'
Leon

-f program-file 从文件中读取awk程序文件,即读取的是'PATTERN{action}'

–file program-file

[leon@vm tmp]$ cat /tmp/awk.program 
/^root/{print $0}
/^leon/{print $0}
[leon@vm tmp]$ awk -f /tmp/awk.program /etc/passwd
root:x:0:0:root:/root:/bin/bash
leon:x:500:500:vm1:/home/leon:/bin/bash

2.1、awk的输出

print item1, item2, …

注意事项:

(1) 各项目之间使用逗号分隔,而输出时则使用输出分隔符分隔;

[leon@vm tmp]$ awk -F: '/\/bin\/bash/{print $1,$7}' /etc/passwd
root /bin/bash
leon /bin/bash
mysql /bin/bash

(2) 输出的各item可以字符串或数值、当前记录的字段、变量或awk的表达式;数值会被隐式转 

换为字符串后输出;

(3) print后面item如果省略,相当于print $0;输出空白,使用pirnt "";

[leon@vm tmp]$ awk -F: '/\/bin\/bash/{print }' /etc/passwd
root:x:0:0:root:/root:/bin/bash
leon:x:500:500:vm1:/home/leon:/bin/bash
mysql:x:495:493::/home/mysql:/bin/bash

printf format,item1,item3,…

注意事项:

(1) 要指定format;

(2) 不会自动换行;如需换行则需要给出\n

(3) format用于为后面的每个item指定其输出格式;

format格式的指示符都%开头,后跟一个字符:
	%c: 显示字符的ASCII码;
	%d, %i: 十进制整数;
	%e, %E: 科学计数法显示数值;
	%f: 显示浮点数;
	%g, %G: 以科学计数法格式或浮点数格式显示数值;
	%s: 显示字符串;
	%u: 显示无符号整数;
	%%: 显示%自身;
	修饰符:
	#:显示宽度
	-:左对齐
	+:显示数值的符号
	.#: 取值精度
	
# awk -F: '{printf "%15s %-20s\n",$1,$7}' /etc/passwd

2.2、awk的变量

2.2.1 内置变量

FS:Field Seperator, 输入时的字段分隔符

[leon@vm tmp]$ awk 'BEGIN {FS=":"}/\/bin\/bash/{print $1,$7}' /etc/passwd
root /bin/bash
leon /bin/bash
mysql /bin/bash

RS:Record Seperator, 输出行分隔符

[leon@vm tmp]$ awk 'BEGIN{RS=":"}{print}' /etc/passwd | head -n 10
root
x
0
0
root
/root
/bin/bash
bin
x
1

OFS: Output Field Seperator, 输出时的字段分隔符;

[leon@vm tmp]$ awk -F: 'BEGIN{OFS="##"}/\/bin\/bash/{print $1,$7}' /etc/passwd 
root##/bin/bash
leon##/bin/bash
mysql##/bin/bash

ORS: Outpput Row Seperator, 输出时的行分隔符;

[leon@vm tmp]$ awk -F: 'BEGIN{ORS="##"}/\/bin\/bash/{print $1,$7}' /etc/passwd 
root /bin/bash##leon /bin/bash##mysql /bin/bash##

NF:Numbers of Field,字段数

[leon@vm tmp]$ awk -F: 'END{print NF}' /etc/passwd
7

NR:Numbers of Record, 行数;所有文件的一并计数;

[leon@vm tmp]$ sudo awk -F: '{print NR}' /etc/passwd /etc/shadow
1
2
...
74
75
76

FNR:行数;各文件分别计数;

[leon@vm tmp]$ sudo awk -F: '{print FNR}' /etc/passwd /etc/shadow
1
2
3
...
36
37
38
1
2
3
...

ARGV:数组,保存命令本身这个字符,awk '{print $0}' 1.txt 2.txt,意味着ARGV[0]保存 

[leon@vm tmp]$ awk 'END{print ARGV[0]}' /etc/passwd
awk

ARGC: 保存awk命令中参数的个数;

FILENAME: awk正在处理的当前文件的名称;

[leon@vm tmp]$ awk 'END{print FILENAME}' /etc/passwd
/etc/passwd

2.2.2 自定义变量 

#awk -v name=leon 'BEGIN{print name}'
# awk 'BEGIN{name="leon";print name}'

2.3、 awk输出重定向

print items > output-file
print items >> output-file
print items | command
# awk -F: '{print $1,$7 >"/tmp/userAndBash.txt"}' /etc/passwd

2.4、 awk操作符

几个就没什么好说的了,算术操作符,赋值操作符,比较操作符,逻辑操作符等都支持

三元条件表达式

selector?if-true-expression:if-false-expression
# awk -F: '{$3>=500?utype="common user":utype="admin or system user";print $1,"is",utype}' /etc/passwd

2.5、 模式 PATTREN

2.5.1 Regexp: 格式为/PATTERN/

仅处理被/PATTERN/匹配到的行;

[leon@vm ~]$ awk -F: '/^root/{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash

2.5.2 Expression: 表达式

其结果为非0或非空字符串时满足条件,仅处理满足条件的行;

[leon@vm tmp]$ awk -F: '$3>=500{print $1,$3,$7}' /etc/passwd
leon 500 /bin/bash
nfsnobody 65534 /sbin/nologin

2.5.3 Ranges: 行范围

此前地址定界,startline, endline,仅处理范围内的行

[leon@vm ~]$ awk -F: '/^root/,/^daemon/{print $1,$7}' /etc/passwd
root /bin/bash
bin /sbin/nologin
daemon /sbin/nologin

2.5.4 BEGIN/END: 特殊模式

仅在awk命令的program运行之前(BEGIN)或运行之后 ,(END)执行一次;

[leon@vm ~]$ sudo awk --re-interval  -F: 'BEGIN{print "--there users  have a passwd--"}/\$1/{print $1}END{print "----end---"}' /etc/shadow
--there users  have a passwd--
root
leon
----end---

2.5.5 Empty:空模式

匹配任意行;这里就不举例子了

2.6、常用的action

 (1) expressions  :表达式

 (2)control statements    :控制语句

 (3)compound statements    :组合语句

 (4)input statements    :输入语句

 (5) output statements    :输出语句

2.7、控制语句

2.7.1 if-else

格式:if (condition) {body} else {body}

[leon@vm ~]$ awk -F: '{if ($3>=500) {print $1,"is a common user"} else {print  $1, "is an admin or system user"}}' /etc/passwd
root is an admin or system user
bin is an admin or system user
...
[leon@vm ~]$ awk -F: '{if (NF>=3){print $0}}' /etc/inittab
id:3:initdefault:

2.7.2 while

格式:while (condition) {while body}

# awk '{i=1; while (i<=NF){printf "%s ",$i;i+=2};print ""}'  /etc/inittab
# awk '{i=1; while (i<=NF){if (length($i)>=6) {print $i}; i++}}' /etc/inittab
length()函数:取字符串的长度

2.7.3 do-while循环

格式:do {body} while (condition)

跟上面例子类似,只不过do中的内容无论如何都会执行一次,不举例

2.7.4 for循环

格式:for (variable assignment; condition; iteration process) {body}

# awk '{for (i=1;i<=NF;i+=2){printf "%s ",$i};print ""}'  /etc/inittab
# awk '{for (i=1;i<=NF;i++){if (length($i)>=6) print $i}}'  /etc/inittab

for循环可用来遍历数组元素:

语法:for (i in array) {body}

awk '{ip[$1]++}END{for (i in ip) {print i,ip[i]}}' /var/log/httpd/access_log

2.7.5 case语句

语法:switch (expression) {case VALUE or /RGEEXP/: statement1;…  default: stementN}

2.7.6 循环控制

break               #跳出当前循环 
continue            #直接进入下一循环

2.7.7 next

提前结束对本行的处理进而进入下一行的处理;

# awk -F: '{if($3%2==0) next;print $1,$3}' /etc/passwd
# awk -F: '{if(NR%2==0) next; print NR,$1}' /etc/passwd

2.8、 数组

array[index-expression]

index-expression :可以使用任意字符串,如果某数组元素在引用时事先不存在,那么在引用时,awk会自动创建此元素并将其赋值为空串;因此要判断数组中是否存在某元素,使用index in array这个格式

要遍历数组中的每一个元素,需要使用以下特殊结构

for (var in array_name) {for body}

其var会遍历array的索引

# awk '{ip[$1]++}END{for (i in ip) {print i,ip[i]}}' /var/log/httpd/access_log

删除数组元素:

delete array[index]

2.9、 awk函数

2.9.1 内置函数

 split(string,array[,fieldsep[,seps]])

 功能:将string表示的字符串以fieldsep为分隔符进行切片,并切片后的结果保存至array为名的数组中;数组小标从1开始

awk 'BEGIN {split("root:x:0:0",user,":");for (i in user) print user[i]}'

此函数有返回值,返回值为切片后的元素的个数

# netstat -tn | awk '/^tcp/{lens=split($5,client,":");ip[client[lens-1]]++}END{for (i in ip) print i,ip[i]}'

length(string)

功能:返回给定字串的长度

# awk '{for (i=1;i<=NF;i++){if (length($i)>=6) print $i}}'  /etc/inittab

substr(string,start[,length])

功能:从string中取子串,从start为起始位置为取length长度的子串;

2.9.2 自定义函数

function  function_name(arg1,arg2,….) {body}

函数调用: function_name(arg1,arg2,…)

[leon@vm tmp]$ echo "3 2" | awk 'function compare(a,b){if(a>b) {return a} else {return b}};{print compare($1,$2)}' 
3

三、总结

作为三剑客之一,awk经常被使用,特别需要注意awk的语法。如,不要把BEGIN和ENG执行的内容当成PATTERN{action},而应该是program,同时function函数也是如此,当成program,否则一不留神就写成'{function function_name(arg…) {body}}'。犯了语法错误。

原创文章,作者:成吉思汗,如若转载,请注明出处:http://www.178linux.com/9291

评论列表(1条)

  • stanley
    stanley 2015-11-18 11:06

    果断置顶,以前也做过awk的技能分享,时间长也忘的差不多了。加油!

联系我们

400-080-6560

在线咨询

工作时间:周一至周五,9:30-18:30,节假日同时也值班

QR code