1. awk基本介绍

awk是一种弱类型的编程语言,所以在使用变量时,其无需事先声明变量类型。
功能说明:gawk - pattern scanning and processing language
awk是GNU项目,所以其实际的命令为gawk,因awk命令的二进制文件链接到gawk,所以awk命令亦是gawk命令。

1.1 描述

Gawk is the GNU Project's implementation of the AWK programming language. It conforms to the definition of the language in the POSIX 1003.1 Standard. This version in turn is based on the description in The AWK Programming Language, by Aho, Kernighan, and Wein‐berger. Gawk provides the additional features found in the current version of UNIX awk and a number of GNU-specific extensions.

1.2 基本格式

awk [option]  ‘program’  FILE1 FILE2…

program:由PATTERN + { statements }组成,statements多语句之间用分号进行分隔;

命令选项:

-Ffs:fs为指定输入时的字段分隔符,fs可以是字符串或正则表达式,如-F: 
-v var=value:自定义变量,使用时会将该变量传递给awk内部;
-f scripfile:从脚本文件中读取awk命令,脚本文件中为awk语句;

PS:awk可以处理文件中的内容,也可以读取来自stdin的输入。

1.3 常用命令结构

awk  'BEGIN{ statements }{ statements }END{ statements }'  FILE

PS:单引号中的三个组成部分都是可选的,且任一部分都可单独出现。

工作原理:

Step1. 在开始处理FILE或stdin中的内容之前,先执行一次BEGIN{ statements }语句(其仅执行一次);
Step2. 从FILE中读取一行,然后执行{ statements }语句块,再读取第二行,接着执行{ statements }。重复这个循环过程,直到FILE全部被读取完毕;
Step3. FILE内容在被{ statements }处理完毕后,接着执行一次END{ statements }'语句块结束awk命令(其仅执行一次)。

2. 常用功能

2.1 变量

1)内建变量

FS:输入字段分隔符(input filed seperator),默认为空白字符;
OFS:输出字段分隔符(output field seperator),默认为一个空白字符;
RS:输入记录分隔符(input record seperator),默认为一个换行符;
ORS:输出记录分隔符(output record seperator),默认为一个换行符;
NF:字段数(number of field),NF表示每行的字段总数,$NF表示最后一个字段;
NR:记录数或行数(number of record),awk后接单个file表示行号;
FNR:多文件行数(file number of record),awk后接多个文件时,分别显示其行号;
ARGC:命令行参数个数;
ARGV:参数数组,元素包含命令行所给出的各参数;
$0:表示file中当前行的内容;
$n:表示file中记录的第n个字段;

:awk在引用变量时,无需加$符号,$n、$0、$NF等特殊变量除外。
2)自定义变量
a. 使用选项-v 定义

awk –v var=value  #变量名区分字符大小写

b. 在program中直接定义

awk ‘var=value’

因awk是弱类型语言,所以若一个变量未赋值时即默认为空值,若该变量参与运算,其初始值则为0:

[root@Study ~]# cat /etc/issue
CentOS release 6.9 (Final)
Kernel \r on an \m

[root@Study ~]# awk '{a=b+2;print $a}' /etc/issue
release
\r

PS:其中b=0,a=0+2。

2.2 运算操作符

1)算术运算符
加 +、减 -、乘 *、除 /、幂 ^、取余%
2)赋值运算符
=、+=、-=、=、/=、^=、%=、++、--
如:a+=5等价于a=a+5,其它亦同。
:i++和++i虽都等于i=i+1,但区别如下:
  i++ :先引用后增加,先在i所在的表达式中使用i的当前值,后让i加1;
  ++i :先增加后引用,让i先加1,然后在i所在的表达式中使用i的新值;
  i+=a:相当于i=i+a。i=i+a 比 i+=a多了一次对变量 i 的运算,后者效率高;
3)比较运算符
小于<、小于等于<=、大于>、大于等于>=、不等于!=、等于==
4)逻辑运算符
|| :逻辑或
&&:逻辑与
! :逻辑非

2.3 其它操作符

1)正则模式匹配符
~:匹配正则
!~:不匹配正则
2)C条件表达式:(? 和 : 结合)

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

2.4 运算符优先级

下表中级别越高,则优先级越高。
文本三剑客之awk基础篇

3. program部分

program组成部分:PATTERN + { statements }

3.1 PATTERN:模式

1)/正则表达式/:仅处理能被正则模式匹配到的行;
2)关系表达式(relational expression):使用运算符进行操作;
 运算结果有“真”有“假”,其仅处理为“真”的结果,“真”表示为非0值或非空字符串。
3)行范围:搭配NR使用
4)空模式:awk默认模式,其匹配file的每一行
5)BEGIN/END模式

3.2 {statements}:多命令语句

以下为语句的组成部分:
1)一般表达式;
2)变量或数组赋值;
3)输入输出语句;
4)函数调用;
内置:
 int( x ):返回 x 的截断至整数的值;
 rand( ):返回任意数字 n,其中 0 <= n < 1;
 sin( x ):返回 x 的正弦;x 是弧度;
自定义:function_name(argu1,argu2…)
5)控制流语句
if语句、while语句、do-while语句和for语句等

3.3 打印输出

3.3.1 print普通输出

PS:print为普通输出,默认会自动换行。
格式:{print item1,item2…}
1) item之间用逗号隔开,输出时则为一个空白字符(OFS);
2) item可以是字符串、数值、变量、当前记录的字段($n)或awk的表达式;
3) 若省略item,即{print}相当于{print $0}。

3.3.2 printf格式输出

PS:printf会进行格式化输出,默认不自动换行,换行需加换行符。
格式:{printf FORMAT,item1,item2…}
1) FORMAT为格式符,其必须给出;
2) 若要换行,需在FORMAT加上换行符“\n”;
3) 若有多个item时,要在FORMAT中分别指定对应item的格式化符号。
格式符

 %c:显示字符的ASCII码
 %d和%i:显示有符号的十进制整数
 %e和%E:科学计数法数值显示
 %f:浮点数(包括float和doulbe)
 %g和%G:以科学计数法或浮点形式显示数值
 %o:八进制整数
 %u:显示无符号十进制整数
 %x和%X:十六进制整数
 %p:指针
 %s:字符串
 %%:显示%自身

修饰符

-:左对齐,如%-s
+:显示数值的符号
n[.n]:n表示数字,第一个n控制字符或数值位数,第二个n则为小数点后的精度,如:%3.2f

转义字符

 \b:退格键(backspace)
 \n:换行
 \r:回车,即Enter键
 \t:水平制表符(tab)
 \v:垂直制表符(tab)
 \xnn:将nn两位数的数字,转换为字符

4. 练习操作

1)通过定义外部变量的方式指定输入输出分隔符

~]# awk -v FS=":" -v OFS="++" '{print $1,$3,$6}' /etc/passwd

~]# awk -F: -v OFS="++" '{print $1,$3,$6}' /etc/passwd
root++0++/root
bin++1++/bin
daemon++2++/sbin
…..

2)FNR和NR的区别:

[root@Study ~]# awk '{print NR,$0}' /etc/issue /etc/redhat-release
1 CentOS release 6.5 (Final)
2 Kernel \r on an \m
3 
4 CentOS release 6.5 (Final)
[root@Study ~]# awk '{print FNR,$0}' /etc/issue /etc/redhat-release
1 CentOS release 6.5 (Final)
2 Kernel \r on an \m
3 
1 CentOS release 6.5 (Final)

3)NF和$NF的区别:

[root@Study ~]# echo -e "line1 f1 f2\nline2 f3 f4\nline4 f5 f6" | awk '{print NF,$NF,$(NF-1)}'
3 f2 f1
3 f4 f3
3 f6 f5

4)ARGC和ARGV操作

[root@Study ~]# awk 'BEGIN{print ARGC}' /etc/issue /etc/passwd /etc/fstab 
4
[root@Study ~]# awk 'BEGIN{print ARGV[2]}' /etc/issue /etc/passwd /etc/fstab 
/etc/passwd
[root@Study ~]# awk 'BEGIN{print ARGV[0]}' /etc/issue /etc/passwd /etc/fstab 
awk

5)在program中进行变量赋值:

~]# awk 'BEGIN{var="Hello World";print var}'
Hello World

6)printf的用法

~]#  awk -F: '{printf "Username:%-10s, UID:%i\n",$1,$3}' /etc/passwd |tail -5
Username:nobody    , UID:99
Username:vcsa      , UID:69
Username:saslauth  , UID:499
Username:postfix   , UID:89
Username:sshd      , UID:74

7)模式匹配

~]#  awk -F: '/^root/,/^sync/{print $1}' /etc/passwd
root
bin
daemon
adm
lp
sync
[root@Study ~]# ifconfig eth0 | awk -F"[ :]+" '/t a/{print $4}'          #-F后接多个分隔符时,使用“[ ]"
192.168.189.128

8)行范围匹配:

~]# awk 'NR<=5' /etc/fstab
~]# awk -F: '(NR>=10&&NR<=20){print $1}' /etc/passwd
~]# awk 'NR>=10&&NR<=20' /etc/passwd
~]# awk 'NR==10,NR==20' /etc/passwd

9)关系表达式

~]# awk -F: '$3>=500{printf "%-15s %5i\n",$1,$3}' /etc/passwd
rick              500
superlong         501
boy               502
~]# awk '$3=="ext4"{print $1,$2,$3}' /etc/fstab

或以正则模式进行匹配:

~]# awk '$3~/\<ext4\>/{print $1,$2,$3}' /etc/fstab  
UUID=5c573541-a198-4ed1-9b9f-a5ee81147c30 / ext4
UUID=e0038068-bb37-4873-b657-4347a1ffc975 /boot ext4 

10)条件表达式

~]# awk -F: '{$3>=500?user="Common User":user="root or SysUser";printf "%-10s,%5i: %-15s\n",$1,$3,user}' /etc/passwd | tail -6
saslauth  ,  499: root or SysUser
postfix   ,   89: root or SysUser
sshd      ,   74: root or SysUser
rick      ,  500: Common User    
superlong ,  501: Common User    
boy       ,  502: Common User   

11)BEGIN和END模式

[root@Study ~]# seq 5 |awk 'BEGIN{printf "Summation\n=========\n"}{print $0"+";sum+=$1}END{print "=========";print sum}'
Summation
=========
1+
2+
3+
4+
5+
=========
15
[root@Study ~]# seq 100 |awk '{a+=$1}END{print a}'
5050

12)通过读取脚本文件,处理文本内容(以下为取file.txt的偶数列)

[root@Study ~]# cat file.txt 
name yu shu wai
rick 80 86 90
long 68 89 78
jing 76 80 66
jack 66 60 82
[root@Study ~]# awk '{for(i=2;i<=NF;i+=2){printf $i" "}}' file.txt 
yu wai 80 90 68 78 76 66 66 82 [root@Study ~]# 
[root@Study ~]# cat even_field.awk
{for(i=2;i<=NF;i+=2)printf $i" ";print a}
# or
# {for(i=2;i<=NF;i+=2){printf $i" "}print a}
[root@Study ~]# awk -f even_field.awk file.txt 
yu wai 
80 90 
68 78 
76 66 
66 82 

PS:for(i=2;i<=NF;i+=2){printf $i" "}为每行的一个循环,处理完毕后,再执行print a;
print a表示打印一个空值,即换行。