awk使用与实战

awk使用与实战

awk使用与实战

awk为linux中的一款文本报告生成器,用于格式化文本输出,在linux中awk为gawk的链接,下面让我们一起看一下awk的用法

1. 相关基础

  • awk一次读取一行文本,然后按照分隔符切片(默认为空白字符,此时-F可省),保存到内建变量中,如$1,$2等

    image-20200507100646568

  • 其中$0 表示整行内容,$NF表示最后一列,如果想倒数来获取,如获取倒数第二行可以使用$(NF-1),依此类推

  • awk可以理解为过程式编程语言,通常使用的命令为awk内建命令,很少使用bash命令

  • awk本身就有在文本行间循环的功能,其内置的循环功能用于在字段间进行循环

  • awk对于多个空白字符理解为一个字符,不同于cut,但是对于其他非空白分隔符并非如此,如下

    image-20200507100210637

2. 基本用法

# 基本用法为 awk [options] 'program' file ....
	program: PATTERN{ACTION STATEMENTS} #语句之间用分号分隔,如print,printf
	options: -F: 指明分隔符,-v var=value 自定义变量

3. print命令

print item1,item2,...
#注意事项:
	1. 使用逗号进行分割如,$1,$2
	2. 输出的各item可以是字符串,也可以是数值,当前记录的字段,变量或awk的表达式
	3. 如省略了item,相当于print $0
#示例如下图

image-20200507102112791

4. 变量

内建变量
  • FS : 输入列分隔符,默认是空白字符
  • OFS:输出列分隔符,默认为空白字符
  • RS:输入时的换行符,默认是\n
  • ORS:输出时的换行符,默认是\n
  • NF: 字段数量, 字段个数
  • NR:行数,不会分文件显示
  • FNR: 各个文件分别计数,行数
  • FLIENAME:当前文件名
  • ARGC:命令行参数的个数 第0个为awk
  • ARGV:数组,保存的是命令行所给定的参数 同上
自定义变量
  • -v var=value 变量名区服字符大小写
  • 在program区域直接定义
示例代码
# 变量的一定要使用"",其他使用单引号即可
[root@ ~]# awk '{FS=":";print $1}' /etc/passwd  
[root@ ~]# awk 'BEGIN{FS=":";for(i=0;i<ARGC;i++){print ARGV[i]}}' /etc/passwd /etc/fstab 
[root@ ~]# awk '{FS=":";print FNR}' /etc/passwd /etc/fstab 

5. printf命令

用于格式化输出,类似与其他语言的用法

# 格式化输出:printf FORMAT ,item1,item2...
# FORMAT 必须给出
# 不会自动换行,需要显示给出换行控制符\n
# FORMAT中需要分别为后面的每一项item指定一个格式化分隔符
# 格式符:
	%c: 显示字符的ASCII码
	%d,%i: 显示十进制整数
	%e,%E: 科学计数法数值显示
	%f: 显示浮点数
	%g,%G: 以科学计数法,或浮点形式显示数值
	%s: 显示字符串
	%u:无符号整数
	%%:转义符,%自身
# 修饰符
	#[.#] 第一个数字控制宽度,第二个数控制精度,如%3.1f
	- 左对齐,默认右对其
	+ 显示数值的符号
# 示例代码
	[root@ ~]# awk -F: 'BEGIN{printf "-----------USERNAME-----------UID-----------\n"};{printf "UserName:%-15sUID:%10.1f\n",$1,$3}' /etc/passwd   

6.操作符

# 算术操作符:
 x+y, x-y, x*y, x/y, x^y, x%y
	-x
	+x: 转换为数值;
# 字符串操作符:没有符号的操作符,字符串连接
	# 赋值操作符:
		=, +=, -=, *=, /=, %=, ^=++, --
	# 比较操作符:
		>, >=, <, <=, !=, ==
	# 模式匹配符:
		~:是否匹配
		!~:是否不匹配
	# 逻辑操作符:
		&& || !
# 条件表达式
	selector?if-true-exp:if-false-exp
# 函数调用
	f_name(args)
# 示例代码
[root@ ~] awk -F: 'BEGIN{printf "-----------USERNAME-----------UID-----------TYPE---------\n"};{$3>=500?type="ADMIN":type="COMMON";printf "%-15s%-10d%-s\n",$1,$3,type}' /etc/passwd  
[root@ ~] awk 'BEGIN{print length(ARGV)}' /etc/passwd /etc/fstab 

7. PATTREN

  • empty : 空模式匹配每一行

  • /rex/:仅处理能被正则匹配到的行

  • rex:关系表达式;结果有真有假,结果为真的才会被执行

  • line ranges:行范围,不能直接给数字格式,支持/pat1/,/pat2/ 和 (NR>=2&&NR<=10)

  • BEGIN/END:BEGIN{},END{}

    示例代码

    awk -F: '(/^r/){print $1}' /etc/passwd

8. 常用的action

  • Expressions
  • Control statements
  • 组合语句
  • input statements
  • output statements

9. 控制语句

  • if(condition)

  • if(condition) else

  • while(condition)

  • do while(condition)

  • for(expr1;expr2;expr3)

  • 循环支持break [n] 支持跳出n层循环和continue

  • delete array[index]

  • delete array

  • exit

    示例代码

    [root@ ~] awk -F: '{if($NF~"bash"){print $1,$NF}}' /etc/passwd
    [root@ ~] awk '{if(NF>5){print $1}}' /etc/fstab 
    [root@ ~] df -h | awk -F% '/^\/dev/{print $1}' | awk '{if($NF>50){print $1}}'
    

10. Array

关联数组 array[index-exp]

index-exp:

  • 可以使用任意字符串;字符串必须使用双引号

  • 如果某索引不存在,再引用时,awk会自动创建,并初始化为空串,数值计算时当零来使用

  • 如果要判断数组中是否存在某索引,要使用 “index in array”

  • 如果要遍历数组中的每一个元素,要使用循环如for(var in array),其中var的值时array的索引

    # 1. 统计netstat中各状态出现的个数
    [root@ ~]# netstat -tan | awk '/^tcp\>/{state[$NF]++}END{for(i in state){print i,state[i]}}'
    TIME_WAIT 504
    ESTABLISHED 1339
    CLOSING 1
    LISTEN 118
    # 2. 统计nginx access log中每个ip的访问次数
    [root@ ~]# awk '{ip[$4]++}END{for(i in ip){print i,ip[i]}}' nginxfile
    # 3. 统计/etc/fstab文件中每个文件系统类型出现的次数
    [root@ ~]# awk '/^UUID/{fs[$3]++}END{for(i in fs){print i,fs[i]}}' /etc/fstab     
    swap 1
    ext4 3
    # 4. 统计指定文件中每个单词出现的次数
    [root@ ~]# awk '{for(i=1;i<=NF;i++){word[$i]++}}END{for(i in word){print i,word[i]}}' /etc/fstab 
    

11. 函数

awk支持函数,分别为内置函数和自定义函数,这边就简单的说一下常用的内置函数就好了,因为用的不多

  • length 获取字符串长度

  • rand() 返回0-1之间的随机数,小数,同一个awk命令,获取到的是一样的

  • sub(r,s,[t]) 第一次查找替换,r为模式,s被替换的内容,t匹配的原内容

  • gsub(r,s,[t]) 同上,全部替换

  • split(s,a[,r]) 以r为分隔符切割字符串s,并将切割后的结果保存至数组a中

    # 注意awk数组下标从1开始
    [root@ ~]# netstat -tan | awk '/^tcp\>/{split($5,ip,":");count[ip[1]]++}END{for(i in count){print i,count[i]}}'