1.awk简介

AWK是一个很古老的报告生成工具,他的实现是读取文本的每一行,然后对每一行的每一个字段分别进行格式化显示,所以他的内部支持循环,变量,条件判断,数组等.awk又分为nawk(awk—>new awk-->nawk)和gawk(GNU awk)在Linux中默认使用的是gawk.可以使用下列命令查看

[root@station142 ~]# which awk
/bin/awk
[root@station142 ~]# ls -l  /bin/awk
lrwxrwxrwx. 1 root root 4 Feb 10 11:14 /bin/awk -> gawk  #可以看到是gawk

·工作模式:

对输入文本逐行处理,每次读取一行,awk仅仅是将读入的文本进行过滤显示的,不像sed可以修改文件,awk在读入文件的时候.会把每一行的每一个字段保存在一个变量中,分别是第一个字段$1,第二个字段$2....$0表示整行.格式:

预处理,

awk  '条件类型1{动作1}条件类型2{动作2}...'filename,
awk  [options ] 'script' filename
awk  [options] '/pattern/{action}'filename
模式匹配:
地址定界:多个模式/pattern1/,/pattern2/,1个模式/pattern/  #模式要用双斜线,这是awk中固定的用法
expression:表达式
>,>=,<,<=,==,!=,~
BEGEN:在每一行执行做预处理,执行前操作
END:执行后操作
awk  '条件类型1{动作1}条件类型2{动作2}...'filename,
awk  [options ] 'script' filename
awk  [options] '/pattern/{action}'filename
模式匹配:
地址定界:多个模式/pattern1/,/pattern2/,1个模式/pattern/  #模式要用双斜线,这是awk中固定的用法
expression:表达式
>,>=,<,<=,==,!=,~

·awk默认有四种分隔符:

分别是输入输出的行分隔符和字段分隔符,字段分隔符默认是空白字符(一个或多个空格和tab),行分隔符默认是$,注:cut是把一个空格或tab当成分隔符

·awk内置变量

NF:字段数,每一行拥有的字段总数

FS:FIled Separator读入行时使用的字段分割符

OFS:Output FIled Separator输出行时使用的字段分隔符

·有趣的示例1

[root@station142 ~]# cat /etc/passwd |awk '{FS=":"} $3 < 10 {print $1 $3}'
root:x:0:0:root:/root:/bin/bash  #两条语句值是差了一点点,第一行显示就不一样了
bin1
daemon2
...
[root@station142 ~]# cat /etc/passwd |awk  -F : ' $3 < 10 {print $1 $3}'
root0                           #同上面查询结果相比较
bin1
daemon2
...
#可以看出,第一条命令没有正确显示出来,这是因为我们读入第一行的时候,那些变量$1, $2... 默认还是以空格键为分隔符,所以虽然我们定义了 FS=":" 了, 但是仅能在第二行后才开始生效。第二条语句一开始就定义以":"分割了,所以不会出现这种情况

·有趣的示例2

[root@station142 ~]# df -h | awk -F "" '{print $1,$3}'  #仅仅是因为双引号里面没有空格
F l
/ e
t p
/ e
/ e
/ e
[root@station142 ~]# df -h | awk -F " " '{print $1,$3}'  #和上面的比较,这个双引号里面有空格
Filesystem Used
/dev/mapper/vg0-root 10G
tmpfs 0
/dev/sda1 35M
/dev/mapper/vg0-usr 3.8G
/dev/mapper/vg0-var 424M

·初级题库集中营

awk '/^root/{print $1}' /etc/passwd        #显示指定文件下root开头的行
awk -F : '/^root/{print $1}' /etc/passwd   #显示指定文件下root
awk -F : '/^root/{print $1$7}' /etc/passwd #显示指定文件件root开头,:分割,的第一个和第七个字段,并且显示的结果没有间隔
awk -F : '/^root/{print $1,$7}' /etc/passwd #显示指定文件件root开头,:分割,的第一个和第七个字段,并且显示的结果以空格分割
awk -F : '/^root/{print "this is username:" ,$1,"this is a shell:" ,$7}' /etc/passwd      #额外显示一些东西的
df -h | awk '/^\//{print $1,$3}'    #以斜线开头的,斜线要转义
cat /etc/passwd |awk '{FS=":"} $3 < 10 {print $1 $3}' #这个显示和下面的自己对比下,要是以后自己回来看忘记了,就试下,不解释了
cat /etc/passwd |awk  -F : ' $3 < 10 {print $1 $3}'   #同上面的
cat /etc/passwd | awk -F : '$3>=500{print $1}'        #和上面的差不多,自己试
awk -F : '$7~/bash$/{print $1,$7}' /etc/passwd   #模式匹配和算术表达式都用上了,以后回来看肯定忘记的
awk -F : '/^root/{print "this is username:" ,$1"\n","this is a shell:" ,$7}' /etc/passwd  #我都懒得跟你讲了,都说是练习题了
awk '{print $NF}' /etc/inittab  #打印最后一个字段
cat /etc/passwd |awk '{FS=":"} $3 < 10 {print $1 "\t" $3}'  #好像是这个和下面的做对比
cat /etc/passwd |awk  -F : ' $3 < 10 {print $1 "\t" $3}'    #这个和上面的做对比
awk -F : 'BEGIN{print "UserName\n-----------------------"}$3>500{print $1}' /etc/passwd   #预处理
awk -F : 'BEGIN{print "UserName\n-----------------------"}END{print"------------------\nthree user"}$3>500{print $1}' /etc/passwd  #之后处理,BEGIN和END放的位置不影响
awk  'BEGIN{FS=":" ; OFS=":"} $3>500{print $1,$3}' /etc/passwd  #以:分割输入.以:分割输出
awk -F : '$3>500{print $1}' /etc/group  #不说
awk -F : '$NF=="/sbin/nologin"{print $1}' /etc/passwd  #显示最后一个字段,NF是awk的内置变量
awk -F "=" '{print $2}' /etc/sysconfig/network-scripts/ifcfg-eth0 #不说
awk -F "=" '!/^#|^$/{print $1}' /etc/sysctl.conf  #不想说
awk -F "=" '!/^#/{print $1}' /etc/sysctl.conf  #和下面的对比,我也不明白为什么下面的可以去掉空白行,有懂的朋友留个言,谢谢
awk -F "=" '/^[^#]/{print $1}' /etc/sysctl.conf这种会滤过空白行,注意
ifconfig eth0 | awk '/inet addr/{print $2}' |awk -F : '{print $2}' #只显示ip

·awk进阶

awk的工作模式是循环,逐行遍历每一行,BEGIN在遍历开始之前执行一次,END在遍历结束之后,命令退出之前执行一次.

·print

print是简单打印工具

·printf

printf是格式化输出工具,使用格式:printf fromat item1,item2,...;printf与print的最大不同是,printf需要指定format,format用于指定item的显示格式,printf语句不会自动打印换行符:\n

format格式的指示符都以%开头,后跟一个字符;如下:
%c: 显示字符的ASCII码;
%d, %i:十进制整数;
%e, %E:科学计数法显示数值;
%f: 显示浮点数;
%g, %G: 以科学计数法的格式或浮点数的格式显示数值;
%s: 显示字符串;
%u: 无符号整数;
%%: 显示%自身;
修饰符:
N: 显示宽度;
-: 左对齐;
[root@station142 ~]# awk -F : '{printf "%-20s %s\n",$1,$NF}' /etc/passwd
+:显示数值符号
[root@station142 ~]# awk -F : '{printf "%+20s %s\n",$1,$NF}' /etc/passwd
[root@station142 ~]# awk 'BEGIN{num1=23;printf "%d\n",num1}'
[root@station142 ~]# awk 'BEGIN{num1=23;num2=21;printf "%d,%d\n",num1,num2}'
printf打印的并不的item,而是format,所以后面的item要和前面的format一一对应

·awk编程语言:

awk支持变量,数组等数据类型,支持选择,循环的执行顺序,awk本身就是逐行循环整个文件的,这里的循环指的是一行当中的循环,不是整个文件的循环,支持内置函数,自定义函数

[root@station142 ~]# awk -F : '/root/{print 3}' /etc/passwd #打印两个3,因为可以匹配到两行

·awk内置变量

RS:Record separator,输入文本信息所使用的换行符

[root@station142 ~]# awk 'FS=" "{print}' /etc/passwd  #请自行敲代码测试

ORS:Output Row Separator,输出文本信息所使用的换行符

[root@station142 ~]# awk 'BEGIN{FS=":" ; ORS="----------"}{print}' /etc/passwd   #请自行动手试试

NR:The Number Of input records:awk命令处理的记录数,如果有多个文件,这个数目会把处理的多个文件中行统一计数;例:

[root@station142 ~]# awk '{print NR,$0}' /etc/fstab /etc/issue  #会把两行连接显示

FNR:与NR不同的是,FNR用于记录正处理的行是当前这一文件中被总共处理的行数;例

[root@station142 ~]# awk '{print FNR,$0}' /etc/fstab /etc/issue  #每行单独记录,可以自己试试就知道效果了

ARGV:数组,保存命令行本身这个字符串,如awk'{print $0}' a.txt b.txt这个命令中,ARGV[0]保存awk,ARGV[1]保存a.txt;

[root@station142 ~]# awk -F : '/root/{print $1 "is a user in  "ARGV[1] }' /etc/passwd    #请自行测试

ARGC:awk命令的参数个数,下面的都是看不懂的...BEGIN的作用???

[root@station142 ~]# awk  'BEGIN{print ARGC}' /etc/passwd /etc/fstab /etc/fstab   #请自行尝试两种方式,我也不知道结果为什么是这样
[root@station142 ~]# awk  '{print ARGC}' /etc/passwd /etc/fstab /etc/fstab   #请自行尝试两种方式,我也不知道结果为什么是这样
[root@station142 ~]# awk 'BEGIN{num1=20;num2=30}{print num1+num2}' /etc/issue
50
50
50
50
50
50
[root@station142 ~]# awk 'BEGIN{num1=20;num2=30;print num1+num2}'
50
[root@station142 ~]#

FILENAME:awk命令处理的文件的名称

[root@station142 ~]# awk  '{print $0 , "in" ,FILENAME}' /etc/issue /etc/fstab   #显示$0来至哪个文件

ENVIRON:当前shell环境变量及其值的关联数组

关联数组:不使用数组元素的默认数字下标,而是单独的给数组中的每个元素重新以字符串命名,关联数组要使用-A,我就知道你回来看的时候会忘记,自己man declare,对于awk来说,关联数组的下标要使用""

[root@station142 ~]# awk 'BEGIN{a["mon"]="monday";a["sun"]="sumday";print a["mon"]}' #我也不知道为什么要用BEGIN啊,哪位大神来和我说说
#知道了.awk一定要等一个文件来循环然后输出指定次数的行数,比如给/etc/fstab就输出 /etc/fstab 行数的a["mon"],不给的话,就只能先给个BEGIN,输出一次

·控制语句:

·if-else

简单类型:selector?if-true-exp:if-false-exp

[root@station142 ~]# awk -v num1=43 -v num2=34 'BEGIN{num1>num2?max=num1:max=num2;print max}'

语法:if (condition) {then-body} [else {else-body}]

[root@station142 ~]# awk -F : '{if($3<500) {printf "%-20s %s",$1,"is a admin\n"} else {printf "%-20s %s" ,$1, "is commman\n"} }
[root@station142 ~]# awk -F : -v sum=0 '{if ($3>=500) sum++}END{print sum}' /etc/passwd   #统计当前系统中uid大于等于500的用户数
awk -F: '{if ($1=="root") print $1, "Admin"; else print $1, "Common User"}' /etc/passwd
awk -F: '{if ($1=="root") printf "%-15s: %s\n", $1,"Admin"; else printf "%-15s: %s\n", $1, "Common User"}' /etc/passwd
awk -F: -v sum=0 '{if ($3>=500) sum++}END{print sum}' /etc/passwd

·while

语法:while (condition) {statement1;statement2;...}

awk -F: '{i=1;while (i<=3) {print $i;i++}}' /etc/passwd
awk -F: '{i=1;while (i<=NF) { if (length($i)>=4) {print $i}; i++ }}' /etc/passwd
awk '{i=1;while (i<=NF) {if ($i>=100) print $i; i++}}' hello.txt
[root@station142 ~]# awk -F : '{i=1;while(i<=5){printf "%s ",$i;i+=2}printf "\n"}' /etc/passwd   #上午看的下午就块忘了,记住,awk的命令只要用到if,while等都是以行为单位的,就是在一行内的循环,而不是整段

·do-while

语法: do {statement1, statement2, ...} while (condition)

awk -F: '{i=1;do {print $i;i++}while(i<=3)}' /etc/passwd
awk -F: '{i=4;do {print $i;i--}while(i>4)}' /etc/passwd

·for

语法:for ( variable assignment; condition; iteration process){ statement1, statement2, ...}

awk -F: '{for(i=1;i<=3;i++) print $i}' /etc/passwd
awk -F: '{for(i=1;i<=NF;i++) { if (length($i)>=4) {print $i}}}' /etc/passwd
[root@station142 ~]# awk 'BEGIN{a[0]="ab";a[1]="ac";for(i=0;i<=1;i++) {print a[i]}}'

要遍历数组的每一个元素的值,要使用如下方法

要遍历数组中的每一个元素,需要使用如下的特殊结构:
for (var in array) { statement1, ... }
其中,var用于引用数组下标,而不是元素值;
awk -F: '$NF!~/^$/{BASH[$NF]++}END{for(A in BASH){printf "%15s:%i\n",A,BASH[A]}}' /etc/passwd   #看不懂就背呗
[root@station142 ~]# netstat -tn | awk '/^tcp/{state[$NF]++}{for (A in state) {printf "%15s %d\n",A,state[A]}}'  #遍历所有$NF结尾的字段,并且保存在state数组中,然后遍历该数组,并打印
#突然顿悟?首先让state[$NF]自加,然后用A遍历state的数组,就可以统计数组中的元素了.之前的疑惑是以为数组中不能有相同的元素:数组 array[1,2,1,1,2,1]
这个就是array[1]出现一次就自加一次,总共就是4次,array[2]两次,在awk中数组事先没有定义一个元素,他的值默认为0
一开始数组为0,array1 出现一次array1=1,两次array=2...array=4
array2=2
也相当于统计数组中相同元素的个数,大概这样理解吧,以后搞明白了再回来看

·case

语法:switch (expression) { case VALUE or /REGEXP/: statement1, statement2,... default: statement1, ...}

·break和continue

常用于循环或case语句中,也仅仅是对字段起效,awk中语句的遍历都是字段

·next与上面两个不一样,这个是跳过整行的

提前结束对本行文本的处理,并接着处理下一行;例如,下面的命令将显示其ID号为奇数的用户:
# awk -F: '{if($3%2==0) next;print $1,$3}' /etc/passwd

·内置函数

·aplit

# netstat -ant | awk '/:80\>/{split($5,clients,":");IP[clients[1]]++}END{for(i in IP){print IP[i],i}}' | sort -k2  -rn | head -50   #以80端口结尾的,切割第五个字段,切割的时候以:为分隔符,并且保存在clients变量中,在把前面的clients中的第一个元素当作IP的下标,开始统计,然后以第二个字段排序,只显示前50个
# df -lh | awk '!/^File/{split($5,percent,"%");if(percent[1]>=20){print $1}}'   #自己看看咯,看看有没有忘记
awk '/GET/{split($7,s,"?");res`s1`++}END {for (i in res){printf "%s %d\n",i,res[i]}} ' access/log  #实验的时候可以看到有点行是没有"?"的,没有问号他也一样处理
# netstat -ant | awk '/:80\>/{split($5,clients,":");IP[clients[1]]++}END{for(i in IP){print IP[i],i}}' | sort -k2  -rn | head -50   #以80端口结尾的,切割第五个字段,切割的时候以:为分隔符,并且保存在clients变量中,在把前面的clients1当作IP的下标,开始统计,然后以第二个字段排序,只显示前50个
# df -lh | awk '!/^File/{split($5,percent,"%");if(percent[1]>=20){print $1}}'   #自己看看咯,看看有没有忘记

·length([string])

功能:返回string字符串中字符的个数;


·substr(string, start [, length])

功能:取string字符串中的子串,从start开始,取length个;start从1开始计数;


·system(command)

功能:执行系统command并将结果返回至awk命令


·systime()

功能:取系统当前时间


·tolower(s)

功能:将s中的所有字母转为小写


·toupper(s)

功能:将s中的所有字母转为大写


·用户自定义函数


自定义函数使用function关键字。格式如下:


function F_NAME([variable])

{

statements

}


函数还可以使用return语句返回值,格式为“return value”。

注:如果仅仅是为了文本过滤,而不是打印报告,务必优先使用grep,因为grep的性能比awk好很多,写的真是乱啊.....