学习思路:
AWK命令行书写由BEGEN Program END三部分构成

BEGEN:在AWK读取输入流文本行处理之前执行,用于初始化变量、定义输出表头信息
Program:定义如何处理读的数据,由两部分构成:pattern{action statements;..},由模式和动作构成,匹配模式则执行动作。
END在文件逐行处理完成后执行END语句,用于统计结果、生成报告

处理流程:
AWK将处理对象视为二维表逐行读取逐行处理,默认以空白符作为分隔符(可以使用 -F指定分隔符),将文件内容分隔为多个域(可以理解为列或字段),一行称为一条记录,每条记录由多个字段构成。这些行与列被映射成了AWK的内置变量(可以通过-v选项自定义变量),因此用户就可以通过这些内置变量来定位处理对象中的内容进行处理,简单理解为:基于行读取、基于列进行处理并输出。

语法:

awk [options] 'program' var=value file…

awk [options] -f programfile var=value file…

awk [options] 'BEGIN{action;… }pattern{action;… }END{action;… }' file ...

每个语句块都有

常用选项

-F "分隔符" 用于指定读取输入流文本行中用到的字段分隔符,如果不指则默认以空白符为分隔符,并自动压缩连续的多个空白符视为一个空白符。
-f file 从指定文件中读取通用语句块
-v var=value 用于变量赋值

内置变量:
AWK将处理对象视为二维表逐行读取,逐行处理,一行为一条记录,一列为个字段,行与列被映射成了AWK的内置变量,所以用户才可以像处理二维表格一样处理数据。因此AWK的常用内置变量也就以行和列的维度就进行定义。

行列维度内置变量: AWK将读入的文本行以指定分隔符分割为多个列,分别映射为内置变量$1,$2,$3...$n,即$1代表第1列,$2代表第2列,以此类推。注意:$0代表所有列,如果语句块中没有定义action动作则默认动作为print $0,即打印所有列。

NF:代表被分隔的字段数,即列数
NR: 一行为一条记录,行号
FNR:AWK可以将多个文件作为输入流,如果希望每个文件分别输出独立的行号则用FNR
FILENAME:AWK可以将多个文件作为输入流,如果希望输出行中输出文件名则用FILENAME

分隔符:
AWK处理输入流时可以用-F选项指定分隔符,但一定会满足用户的使用需求,例:在打印输出时,打印输入时用的分隔符,就需要手动指定很麻烦,AWK同样想到了用户的这一需求,提供了分隔符相关的内置变量。AWK的处理流程就是输入流处理后生成输出流,所以分隔符也就有了输入分隔符、输出分隔符。

记住几个单词:F:Field S:Separated O:output R:record
FS:输入分隔符
OFS:输出分隔符
RS:输入记录分隔符
ORS:输出记录分隔符
FNR:各文件分别计数,记录号
FILENAME:当前文件名

其它变量
ARGC:命令行参数的个数
ARGV:数组,保存的是命令行所给定的各参数

自定义变量
自定义变量一般用于数据辅助处理(如控制语句),报表输出美化等。自定义变量有两种定义方式:

  1. -v选项定义:
  2. 无事先声明,直接写在program语句块中。

格式化输出
AWK的强项就是报表输出功能,printf命令可以对处理后的数据进行格式化输出,起到美化报表的效果。在格式化输出过程中要注意以下几点:

  1. 必须指定FORMA
  2. 不会自动换行,需要显式给出换行控制符\n
  3. FORMAT中需要分别为后面每个item指定格式符

格式符

  1. %c:显示字符的ASCII码
  2. %d, %i:显示十进制整数
  3. %e, %E:显示科学计数法数值
  4. %f:显示为浮点数
  5. %g, %G:以科学计数法或浮点形式显示数值
  6. %s:显示字符串
  7. %u:无符号整数
  8. %%:显示%自身

修饰符

  1. #[.#] 第一个数字控制显示的宽度;第二个#表示小数点后精度,%3.1f
  2. -左对齐(默认右对齐) %-15s
  3. +显示数值的正负符号 %+d

例:

  1. 指定输出$1对应的输出修饰为左对齐,输出域宽度为20个字符,$3输出域宽度为10个字符,并换行

    awk -F: '{printf "%-20s %10d\n",$1,$3}' /etc/passwd

  2. 指定Username:之后输出$1,格式为左对齐,输出域宽度为15个字符,UID:之后输出$3

    awk -F: '{printf "Username: %-15s,UID:%d\n",$1,$3}' /etc/passwd


AWK中的操作符

1. 算术操作符

注意:算术运行表达式输写时变量不需要加$符号引用,直接输写

x+y, x-y, x*y, x/y, x^y, x%y
-x:转换为负数
+x:将字符串转换为数值

2. 赋值操作符

注意:要注意在输写自加自减赋值表达式时,i++与++i的区别

=, +=, -=, *=, /=, %=, ^=,++, --
i++ 先赋值,再自加,例在循环语句中i=0,A=i++,则A的结果为0,i的结果为1
++i 先自加,再赋值,例在循环语句中i=0,A=++i,则A的结果为1,i的结果为1

3. 比较操作符

注意:在应用的时候需要注意==与=的输写,有时候疏忽可能会出现书写错误。

==, !=, >, >=, <, <=

例:显示$3字段等于0的行

[root@Centos8 ~]#awk -F: '$3==0{print $1,$3}' /etc/passwd

4. 模式匹配符

~:左边是否和右边匹配,意为:包含
!~:是否不匹配 意为:不包含

例:

awk '$1 ~ "^root"' /etc/passwd 文件中第一字段是否包含root
awk '$0 !~ /root/' /etc/passwd 文件不包含root的行

5. 逻辑操作符

与:&& 意为:并且
或:|| 意为:或者
非:!  意为:取反

例:基于UID过滤显示用户

awk -F: '$3>=0 && $3 <=500{print $1,$3}' /etc/passwd
awk -F: '$3==0 || $3>=1000 {print $1},$3' /etc/passwd
awk -F: '!($3>=500) {print $1,$3}' /etc/passwd

6.条件表达式(三目表达式)

语法:selector?if-true-expression:if-false-expression

selector:判断条件
if-true-expression:如果为真则执行该语句
if-false-expression:如果为假则为执行该语句

例:条件赋值

awk -F: '$3>=1000?usertype="common user":usertype="sysuser";prin $1,$3' /etc/passwd

AWK中的模式

从输入流中读取行,并基于模式匹配,如果满足条件则执行相应的处理。

空模式:如果未指定:匹配每一行
正则表达式:/regular expression/:仅处理能够模式匹配到的行,需要用/ /括起来
关系表达式:结果为“真”才会被处理,非0非空即为真。
行范围匹配:起始位置,结束位置;/pattern1/,/pattern2/ 不支持直接给出数字格式,pattern可以是正则表达式
BEGIN/END模式:

例:
匹配以root开头的行到以nobody开头的行之间的所有行
awk -F: '/^root\>/,/^nobody\>/{print $1}' /etc/passwd


控制语句

AWK中控制语名输写原则:

1. 控制语句通体被大括号{}包裹
2. 判断条件由小括号()包裹
3. action作为控制语句了执行语句块,为了逻辑清晰建议action由{}包裹

1. if条件判断

语法1:if与else分体输写,用分号分隔:

'{if(condition) {statements;…};else {statements;…}}' if与else用分号分隔,然后整个语块被{}包裹

例:awk -F: '{if($3>=1000){print "common:",$1} else {print "sysuser:",$1}}' /etc/passwd

语法2:if与else合体输写,不用分号分隔

'{if(condition) {statements;…} else {statements;…}}' if与else不用分号分隔,然后整个语块被{}包裹

例:awk -F: '{if($3>=1000) {print "Common user:",$1} else {print "root or Sysuser:",$1}}' /etc/passwd

语法3:if嵌套

'{if(condition) {statements;…} else if(condition) {statements;…}else {statements;…}}'
'{if(condition) action...;else if(condition) action...;else action...}' if else语句块之间用分号分隔

例:awk 'BEGIN{test=100;if(test>90){print "very good"} else if(test>60){ print "good"}else{print "no pass"}}'

例:awk 'BEGIN{test=10;if(test>80)print "very Good";else if(test>60)print "Good";else print "no pass"}'

2. for循环

for(expr1;expr2;expr3) {statements;…}

例:awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg

3. while循环

语法:while(conditon) {statments;…}

例:awk '/^[[:space:]]linux16/{i=1;while(i<=NF){print $i,length($i); i++}}' /etc/grub2.cfg
例:awk '/^[[:space:]]
linux16/{i=1;while(i<=NF){if(length($i)>=10){print $i,length($i)}; i++}}' /etc/grub2.cfg

4. do...while

do...while与while的区别:do...while雁过拔毛不管条件满不满足至少执行一次;while在条件不满足时可以一次都不执行

语法:do {statements;…} while(condition)

例:awk 'BEGIN{ total=0;i=0;do{ total+=i;i++;}while(i<=100);print total}'