Linux之awk命令
1.awk简介
功能:awk就是把文件逐行的的读入,以空格为默认分隔符将每行切片,切片的部分再进行各种分析处理
工作模式:
1、行工作模式,读入一行文件,存在“$0”里
2、使用内置变量FS(字段分隔符)分割一行,存到$1-$100
3、输出时也是用内置变量OFS,输出该行
与sed主要异同:
相同点:
1.他们的语法基本相同
2.他们都是流编辑器,工作方式都是读入一行,处理动作,输出结果
3.他们都使用正则表达式作模式匹配
4.他们都允许使用脚本
不同点:
1.sed主要处理行,awk主要处理列
2.sed处理动作必须与文件内容相关,awk处理动作可以与文件内空无关,并且awk的动作要用{}括起来.
小结:awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得由为强大。
2.awk语法
awk -F|-f|-v 'BEGIN{ } / / {comand1;comand2} END{ }' file
-F 定义列分隔符
-f 指定调用脚本
-v 定义变量
' '引用代码块,awk执行语句必须包含在内
BEGIN{ } 初始化代码块,在对每一行进行处理之前,初始化代码,主要是引用全局变量,设置FS分隔符
{ } 命令代码块,包含一条或多条命令
// 用来定义需要匹配的模式(字符串或者正则表达式),对满足匹配模式的行进行上条代码块的操作
END{ } 结尾代码块,在对每一行进行处理之后再执行的代码块,主要是进行最终计算或输出结尾摘要信息
3.特殊要点
$0 表示整个当前行
$1 每行第一个字段
NF 字段数量变量
NR 每行的记录号,多文件记录递增
FS BEGIN时定义分隔符
RS 输入的记录分隔符, 默认为换行符(即文本是按一行一行输入)
FNR 与NR类似,不过多文件记录不递增,每个文件都从1开始
OFS 输出字段分隔符, 默认也是空格,可以改为制表符等
ORS 输出的记录分隔符,默认为换行符,即处理结果也是一行一行输出到屏幕
FILENAME 文件名
\t 制表符
\n 换行符
~ 匹配,与==相比不是精确比较
!~ 不匹配,不精确比较
== 等于,必须全部相等,精确比较
!= 不等于,精确比较
&& 逻辑与
|| 逻辑或
+ 匹配时表示1个或1个以上
/[0-9][0-9]+/ 两个或两个以上数字
/[0-9][0-9]*/ 一个或一个以上数字
-F'[:#/]' 定义三个分隔符
4.awk输出
4.1 print
awk -F ":" '{print $1"\t"$3}' /etc/passwd
awk -F ":" '{print $1,$3}' OFS="\t" /etc/passwd
awk -F ":" '/root/{print $1,$3}' OFS="\t" /etc/passwd
print要点
各个输出字段之间用逗号分隔,而输出时默认以空白分隔
print后面如果不指定字段,那么就会打印一整行
print输出时默认是有换行符的
4.2 printf
printf —— 可以格式化输出,默认没有换行
使用格式:printf format item1,item2,... ...,itemn
format的指示符都是以%开头的,后面跟一个字符,如:
%s:表示是字符串 string
%d:表示十进制整数
%f:表示浮点数,也就是小数 float
%%:表示%本身
%x:表示十六进制数
%o:表示八进制数
%c :表示字符
N(数字) 表示显示宽度 %5d 注意:指定字段显示宽度时>=该字段最长的长度
-:左对齐,默认是右对齐 %-5s (向左五个字符)
%5.2f:其中5表示总的显示宽度(整数位+小数位),2表示小数的位数
%-5.2f
%.2f:整数位全部保留,小数位保留两位
示例
echo 12.34 15.78 > a.txt
awk -F" " '{printf "%d\n",$1}' a.txt #%d直接取整,不四舍五入
12
awk -F ":" '{printf $3"\t"$4"\n"}' /etc/passwd # \t 制表符 \n 换行 都需要用" "来区分
awk -F ":" '{printf "%s%s\n",$1,$3}' /etc/passwd #%s% printf 后面需要加\n %s%s 默认没有输出分隔符
awk -F ":" '{printf "%d%d\n",$3,$4}' /etc/passwd #%d%d 表示以整数和整数的格式显示
awk -F ":" '{printf "%10s%d\n",$3,$4}' /etc/passwd #%10s%d 表示以字符串和整数的格式向右移动10个字段
awk -F ":" '{printf "%-d%10d\n",$3,$4}' /etc/passwd #-:左对齐
awk '{printf "%.3f\n",$1}' a.txt #%.3f 表示整数位保留,小数位保留3位
5.awk属组
数组:array 关联数组:因为awk数组的下标可以是数值,也可以是字符串
数组的定义:数组名[下标]=值
awk数组用于:收集信息、统计总数、计算次数、记录模式出现的次数等等 awk数组既可以先定义再使用,也可以用到时候再定义。
数组的遍历
特殊的for循环来遍历数组
语法:for (下标 in 数组名){ print 数组名[下标]}
示例1
定义数组,并且打印数组元素
awk 'BEGIN{a[0]="xiaohong";a[1]="xiaolan";print a[0]}'
awk 'BEGIN{a[0]="xiaohong";a[1]="xiaolan";print a[1]}'
打印出所有元素的下标
awk 'BEGIN{a[0]="xiaohong";a[1]="xiaolan";for (i in a)print i;}'
将/etc/passwd/里边的第一类定义成数组
awk -F":" '{{a[NR]=$1;}{print NR,a[NR];}}' /etc/passwd
示例2
[root@docker ~]# cat a.txt
192.168.3.1
192.168.3.2
192.168.3.3
192.168.3.4
192.168.3.5
192.168.3.6
192.168.3.7
192.168.3.1
192.168.3.2
192.168.3.3
192.168.3.3
192.168.3.3
192.168.3.2
192.168.3.3
192.168.3.6
192.168.3.6
192.168.3.3
192.168.3.2
192.168.3.2
利用数组统计每一个IP的访问量
awk '{a[$1]++} END {for(b in a) print a[b],b}' a.txt | sort -nr
6 192.168.3.3
5 192.168.3.2
3 192.168.3.6
2 192.168.3.1
1 192.168.3.7
1 192.168.3.5
1 192.168.3.4
Awk在读取第一行的时候,会读取这个数组,此时的数组是这样的:a[192.168.3.1]++。
此时a[192.168.3.1]的值是未定义的。但是由于后边有运算符号++。Awk会将数字0自动赋值给a[192.168.3.1]。然后在做++运算
此时a[192.168.3.1]做++ ,也就是0+1得到的值为1
那么在读第二个192.168.3.1时,此时a[192.168.3.1]的值已经经过上次运算为1.此时在做一次运算。也就是1+1 现在a[192.168.3.1]的值为2。
总结,最后的值是多少,也就意味着,192.168.3.1运算了多少次,也意味着192.168.3.1出现了多少次。
6.awk变量
FS 字段分隔符,默认是空白
RS 记录分隔符,即行的分隔符
OFS 输出字段分隔符
ORS 输出记录分隔符
NF 当前行的字段数(number field 字段号)
$NF:当前行最后一个字段的值
$(NF-1) 当前行倒数第二个字段的值
NR number record 记录行号
substr 截取指定的字符串
gsub 替换指定字符串
示例1
FS设置域分隔符 等于-F
[root@docker ~]# awk 'BEGIN{FS=":"}{print $1}' /etc/passwd
OFS 输出域分隔符
[root@docker ~]# cat a.txt
192.168.3.1 192.168.3.2
[root@docker ~]# awk 'BEGIN{OFS=","}{print $1,$2}' a.txt
192.168.3.1,192.168.3.2
注意:这种情况需要多列,单列是不行的
NF 打印每行内容有多少列
awk -F":" '{print NF}' /etc/passwd
NR 已读的行记录数
awk '{print NR}' /etc/passwd
示例2
ORS 输出记录分隔符
注明:awk规定,对于文本文件,默认情况下,一条记录对应一行,即记录之间的默认分隔符是换行符;而一条记录又可以分为多个字段,默认字段分隔符为空格。
cat a.txt
192.168.3.1
192.168.3.2
awk 'BEGIN{ORS=","}{print $0}' a.txt
192.168.3.1 ,192.168.3.2 ,
示例3
substr($n,n) 打印指定的列,并且从指定的第n个字符开始打印
substr($n,n,m) 打印指定的列,并且从指定的第n个字符开始打印,到m个字符停止
awk 'BEGIN{FS=":"}{if(NR==1){print substr($NF,1)}}' /etc/passwd
awk 'BEGIN{FS=":"}{if(NR==1){print substr($NF,2)}}' /etc/passwd
awk 'BEGIN{FS=":"}{if(NR==1){print substr($NF,3)}}' /etc/passwd
ifconfig eth0 |awk '{if(NR==2){print substr($2,1,15)}}'
示例4
将第一行的每个字符后面加一个空格
awk 'BEGIN{ORS=" "}{if(NR==1){for(i=1;i<=length();i++){print substr($0,i,1)}print "\n"}}' /etc/passwd
r o o t : x : 0 : 0 : r o o t : / r o o t : / b i n / b a s h
正则匹配替换多个空格为“#”
awk 'BEGIN{array="a b c d"; gsub(/ +/, "#", array); print array;}'
a#b#c#d
7.awk操作符
赋值运算符
= += -= *= /= %= ^= 赋值语句
逻辑运算符
|| 逻辑或
&& 逻辑与
正则运算符
~ ~! 匹配正则表达式和不匹配正则表达式
关系运算符
< <= > >= != == 关系运算符
算术运算符
+ - 加,减
* / & 乘,除与求余
+ - ! 一元加,减和逻辑非
^ *** 求幂
++ -- 增加或减少,作为前缀或后缀
其它运算符
$ 字段引用
空格 字符串连接符
? : C条件表达式
in 数组中是否存在某键值
*awk赋值运算符*
awk 'BEGIN{a=5; a+=5; print a; a*=2; print a; a^=2; print a; a%=2; print a;}'
10
20
400
0
*awk逻辑运算符*
awk 'BEGIN{a=1; b=2; print (a>0), (b<0), (a>5 && b<=2),(a>5 || b<=2);}'
1 0 0 1
*awk正则运算符*
awk 'BEGIN{a="100testa";if(a ~ /^100*/){print "ok";}}'
ok
*awk关系运算符*
awk 'BEGIN{a=11; if(a >= 9){print "ok";}}'
ok
awk 'BEGIN{a="11"; if(a >= 9){print "ok";} else{print "no";}}'
no
*awk算术运算符*
awk 'BEGIN{a="12"; print a, a++,++a; print a+2, a*2, a^2, !a}'
12 12 14
16 28 196 0
*其它运算符*
awk 'BEGIN{a="b"; arr[0]="b"; arr["b"]="c"; print (a in arr); print arr[a];}'
1
c
awk 'BEGIN{a="b"; print a=="b" ? "ok" : "err";}'
ok
echo "a b c d" | awk '{print $1, $3}'
a c
awk 'BEGIN{arr="a b c d"; gsub(/ +/, "#", arr); print arr;}'
a#b#c#d
注明:则匹配替换多个空格为“#”
8.awk正则表达式
字符 | 功能 |
---|---|
+ | 指定如果一个或多个字符或扩展正则表达式的具体值(在 +(加号)前)在这个字符串中,则字符串匹配。命令行:awk ‘/smith+ern/’ testfile将包含字符 smit ,后跟一个或多个 h 字符,并以字符ern 结束的字符串的任何记录打印至标准输出。此示例中的输出是:smithern, harry smithhern, anne |
? | 指定如果零个或一个字符或扩展正则表达式的具体值(在 ?(问号)之前)在字符串中,则字符串匹配。命令行:awk ‘/smith?/’ testfile将包含字符 smit ,后跟零个或一个 h 字符的实例的所有记录打印至标准输出。此示例中的输出是:smith, alan smithern, harry smithhern, anne smitters, alexis |
| | 指定如果以 |(垂直线)隔开的字符串的任何一个在字符串中,则字符串匹配。命令行:awk ‘/allen | alan /’ testfile将包含字符串 allen 或 alan 的所有记录打印至标准输出。此示例中的输出是:smiley, allen smith, alan |
( ) | 在正则表达式中将字符串组合在一起。命令行:awk ‘/a(ll)?(nn)?e/’ testfile将具有字符串 ae 或 alle 或 anne 或 allnne 的所有记录打印至标准输出。此示例中的输出是:smiley, allen smithhern, anne |
{m} | 指定如果正好有 m 个模式的具体值位于字符串中,则字符串匹配。命令行:awk ‘/l{2}/’ testfile打印至标准输出smiley, allen |
{m,} | 指定如果至少 m 个模式的具体值在字符串中,则字符串匹配。命令行:awk ‘/t{2,}/’ testfile打印至标准输出:smitters, alexis |
{m, n} | 指定如果 m 和 n 之间(包含的 m 和 n)个模式的具体值在字符串中(其中m<= n),则字符串匹配。命令行:awk ‘/er{1, 2}/’ testfile打印至标准输出:smithern, harry smithern, anne smitters, alexis |
[String] | 指定正则表达式与方括号内 String 变量指定的任何字符匹配。命令行:awk ‘/sm[a-h]/’ testfile将具有 sm 后跟以字母顺序从 a 到 h 排列的任何字符的所有记录打印至标准输出。此示例的输出是:smawley, andy |
[^ String] | 在 [ ](方括号)和在指定字符串开头的 ^ (插入记号) 指明正则表达式与方括号内的任何字符不匹配。这样,命令行:awk ‘/sm[^a-h]/’ testfile打印至标准输出:smiley, allen smith, alan smithern, harry smithhern, anne smitters, alexis |
,! | 表示指定变量与正则表达式匹配(代字号)或不匹配(代字号、感叹号)的条件语句。命令行:awk ‘$1 ~ /n/’ testfile将第一个字段包含字符 n 的所有记录打印至标准输出。此示例中的输出是:smithern, harry smithhern, anne |
^ | 指定字段或记录的开头。命令行:awk ‘$2 ~ /^h/’ testfile将把字符 h 作为第二个字段的第一个字符的所有记录打印至标准输出。此示例中的输出是:smithern, harry |
$ | 指定字段或记录的末尾。命令行:awk ‘
2
/
y
2 ~ /y
2 /y/’ testfile将把字符 y 作为第二个字段的最后一个字符的所有记录打印至标准输出。此示例中的输出是:smawley, andy smithern, harry |
. (句号) | 表示除了在空白末尾的终端换行字符以外的任何一个字符。命令行:awk ‘/a…e/’ testfile将具有以两个字符隔开的字符 a 和 e 的所有记录打印至标准输出。此示例中的输出是:smawley, andy smiley, allen smithhern, anne |
*(星号) | 表示零个或更多的任意字符。命令行:awk ‘/a.*e/’ testfile将具有以零个或更多字符隔开的字符 a 和 e 的所有记录打印至标准输出。此示例中的输出是:smawley, andy smiley, allen smithhern, anne smitters, alexis |
\ (反斜杠) | 转义字符。当位于在扩展正则表达式中具有特殊含义的任何字符之前时,转义字符除去该字符的任何特殊含义。例如,命令行:/a///将与模式 a // 匹配,因为反斜杠否定斜杠作为正则表达式定界符的通常含义。要将反斜杠本身指定为字符,则使用双反斜杠。有关反斜杠及其使用的更多信息,请参阅以下关于转义序列的内容。 |
示例
统计系统中每种shell的用户有几人
awk -F: '{a[$NF]++}END{for(b in a){print b,a[b]}}' /etc/passwd
/bin/sync 1
/bin/bash 5
/bin/sh 1
/sbin/nologin 31
/sbin/halt 1
/sbin/shutdown 1