介绍
它允许您创建简短的程序,这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报表,还有无数其他的功能。
awk 是一种很棒的语言,它适合文本处理和报表生成,其语法较为常见,借鉴了某些语言的一些精华,如 C 语言等。在 linux 系统日常处理工作中,发挥很重要的作用,掌握了 awk将会使你的工作变的高大上。
AWK原理
这需要一个例子来说明,你将会见到/etc/passwd 文件的内容出现在眼前。
[root@localhost ~]# awk '{print $0}' /etc/passwd [root@localhost ~]# echo "liujunjun"|awk '{print "hello,world"}' [root@localhost ~]# awk '{ print "root" }' /etc/passwd
现在,解释 awk 做了些什么。调用 awk时,我们指定/etc/passwd 作为输入文件。执行 awk 时,它依次对/etc/passwd 中的每一行执行 print 命令。所有输出都发送到 stdout,所得到的结果与执行 cat /etc/passwd 完全相同。现在,解释{ print }代码块。在 awk 中,花括号用于将几块代码组合到一起,这一点类
似于C 语言。在代码块中只有一条 print 命令。在awk 中,如果只出现 print 命令,那么将打印当前行的全部内容。
再次说明,awk 对输入文件中的每一行都执行这个脚本。
AWK常用速查表
运算符 | 说明 |
赋值运算符 | |
= += -= *= /= %= ^= **= | 赋值语句 |
逻辑运算符 | |
|| | 逻辑或 |
&& | 逻辑与 |
正则运算符 | |
~ !~ | 匹配正则表达式和不匹配正则表达式 |
关系运算符 | |
< <= > >= != == | 关系运算符 |
算术运算符 | |
+ - | 加,减 |
* / & | 乘,除与求余 |
+ - ! | 一元加,减和逻辑非 |
^ *** | 求幂 |
++ -- | 增加或减少,作为前缀或后缀 |
其他运算符 | |
$ | 字段引用 |
空格 | 字符串链接符 |
?: | 三目运算符 |
In | 数组中是否存在某键值 |
常用AWK内置变量
变量名 | 属性 |
$0 | 当前记录 |
$n | 当前记录的第 n 个字段 |
FS | 输入字段分隔符 默认是空格 |
RS | 输入记录分割符 默认为换行符 |
NF | 当前记录中的字段个数,就是有多少列 |
NR | 已经读出的记录数,就是行号,从 1 开始 |
OFS | 输出字段分隔符 默认也是空格 |
ORS | 输出的记录分隔符 默认为换行符 |
awk中的正则
元字符 | 功能 | 示例 | 解释 |
^ | 行首定位符 | /^root/ | 匹配所有以 root 开头的行 |
$ | 行尾定位符 | /root$/ | 匹配所有以 root 结尾的行 |
. | 匹配任意单个字符 | /r..t/ |
匹配字母 r,然后两个任意字符,再以 l 结尾的行,比如 root,r33l 等 |
* | 匹配 0 个或多个前导字符(包括回车) | /a*ool/ | 匹配 0 个或多个 a 之后紧跟着 ool 的行,比如 ool,aaaaool 等 |
+ | 匹配 1 个或多个前导字符 | /a+b/ |
匹配 1 个或多个 a 加 b 的行,比如 ab,aab 等 |
? | 匹配 0 个或 1 个前导字符 | /a?b/ | 匹配 b 或 ab 的行 |
[] | 匹配指定字符组内的任意一个字符 | /^[abc] | 匹配以字母 a 或b 或 c 开头的行 |
[^] | 匹配不在指定字符组内任意一个字符 | ^[^abc]/ | 匹配不以字母 a 或 b 或 c 开头的行 |
() | 子表达式组合 | /(rool)+/ | 表示一个或多个 rool 组合,当有一些字符需要组合时,使用括号括起来 |
| | 或者的意思 | /(root)|B/ | 匹配root 或者 B 的行 |
转义字符 | /a/// | 匹配 a// | |
~,!~ | 匹配,不匹配的条件语句 | $1~/root/ | 匹配第一个字段包含字符root 的所有记录 |
x{m} | x 重复m 次 | /(root){3}/ | 需要注意一点的是,root 加括号和不 |
x{m,} | x 重复至少m 次 | /(root){3,}/ | 加括号的区别,x 可以表示字符串也 |
X{m,n} |
x 重复至少 m 次, 但不超过 n 次 |
/(root){5,6}/ |
可以只是一个字符,所以/root{5}/ 表示匹配roo 再加上5 个t,及roottttt |
需要指定参数: -posix 或者 --re-interval 没 有该参数不能使用该模式 |
cat rex.txt smierth,harry smierth,reru robin,tom |
/rootroot{2,}/ 则 表 示 匹 配 rootrootrootroot 等 awk -posix '/er{1,2}/' rex.text smierth,harry smierth,reru |
awk 常用函数表
函数 | 说明 |
gsub( Ere, Repl, [ In ] ) | 除了正则表达式所有具体值被替代这点,它和 sub 函数完全一样地执行,。 |
sub( Ere, Repl, [ In ] ) | 用 Repl 参数指定的字符串替换 In 参数指定的字符串中的由 Ere参数指定的扩展正则表达式的第一个具体值。sub 函数返回替换的数量。出现在 Repl 参数指定的字符串中的 &(和符号)由 In 参数指定的与Ere 参数的指定的扩展正则表达式匹配的字符串替换。如果未指定 In 参数,缺省值是整个记录($0 记录变量)。 |
index( String1, String2 ) | 在由 String1 参数指定的字符串(其中有出现 String2 指定的参数)中,返回位置,从 1 开始编号。如果 String2 参数不在 String1 参数中出现,则返回 0(零)。 |
length [(String)] |
返回 String 参数指定的字符串的长度(字符形式)。如果未给出 String 参数,则返回整个记录的长度($0 记录变量)。 |
blength [(String)] |
返回 String 参数指定的字符串的长度(以字节为单位)。如果未给出 String 参数,则返回整个记录的长度($0 记录变量)。 |
substr( String, M, [ N ] ) |
返回具有 N 参数指定的字符数量子串。子串从 String 参数指定的字符串取得,其字符以 M 参数指定的位置开始。M 参数指定为将 String 参数中的第一个字符作为编号 1。如果未指定 N 参数,则子串的长度将是 M 参数指定的位置到 String 参数的末尾 的长度。 |
match( String, Ere ) | 在 String 参数指定的字符串(Ere 参数指定的扩展正则表达式出现在其中)中返回位置(字符形式),从 1 开始编号,或如果 Ere 参数不出现,则返回 0(零)。RSTART 特殊变量设置为返回值。RLENGTH特殊变量设置为匹配的字符串的长度,或如果未找到任何匹配,则设置为 -1(负一)。 |
split( String, A, [Ere] ) |
将 String 参数指定的参数分割为数组元素 A[1], A[2], . . ., A[n],并返回 n 变量的值。此分隔可以通过 Ere 参数指定的扩展正则表达式进行,或用当前字段分隔符(FS 特殊变量)来进行(如果没有给出 Ere参数)。除非上下文指明特定的元素还应具有一个数字值,否则 A 数 |
AWK实践
[root@localhost ~]# awk '{gsub(/[0-9]+/,"");print}' /etc/passwd #把每行中的数字都过滤掉了,并由空字符代替。 root:x:::root:/root:/bin/bash bin:x:::bin:/bin:/sbin/nologin daemon:x:::daemon:/sbin:/sbin/nologin adm:x:::adm:/var/adm:/sbin/nologin lp:x:::lp:/var/spool/lpd:/sbin/nologin
[root@localhost log]# awk '/Failed/{print $(NF-3)}' secure |sort |uniq -c |sort -nk1
[root@localhost log]# echo hello the word |awk '{print $1,$2,$3}' hello the word
[root@localhost ~]# cat names Tom Savage 100 Molly Lee 200 John Doe 300 [root@localhost ~]# awk '{print $1,$3}' names Tom 100 Molly 200 John 300
[root@localhost ~]# echo hello the word |awk '{print $1,$2,$3}' hello the word
分段分隔符
[root@localhost ~]# awk 'BEGIN{FS=":"}{print $1}' /etc/passwd [root@localhost ~]# awk -F ":" '{print $1}' /etc/passwd
[root@localhost ~]# echo "hello the:word,! " |awk -F "[:,]" '{print $1}'
hello the
awk 'BEGIN{FS=":"}{print $1}' /etc/passwd
[root@localhost ~]# awk 'BEGIN{OFS="-"}{print $1}' /etc/passwd [root@localhost ~]# date |awk '{print "Thu:"$2 " Year:"$1}' Thu:11月 Year:2019年 [root@localhost ~]# awk '{printf "the name is:%-15s ID is %8d ",$1,$3}' names #%-15s打印字符串,左对齐,%8d打印10进制数,右对刘。 the name is:Tom ID is 100 the name is:Molly ID is 200 the name is:John ID is 300 [root@localhost ~]# awk 'BEGIN{OFMT="%.2f";print 1.234567,12E-2}' #打印浮点数,并保留两位小数。 1.23 0.12
[root@localhost ~]# awk '/^root/{print $1}' /etc/passwd root:x:0:0:root:/root:/bin/bash [root@localhost ~]# awk '!/root/' /etc/passwd [root@localhost ~]# awk '$0~/^root/' /etc/passwd root:x:0:0:root:/root:/bin/bash [root@localhost ~]# awk '$NF~/bash$/' /etc/passwd root:x:0:0:root:/root:/bin/bash
[root@localhost ~]# awk -F ":" '$3 == 99{PRINT $1}' /etc/passwd [root@localhost ~]# awk -F ":" '$3 > 100{print $1}' /etc/passwd systemd-network polkitd chrony [root@localhost ~]# awk -F ":" '$1~/root/{print $1}' /etc/passwd root
条件表达式 多条件表达式其实就是一个if/else语句
[root@localhost ~]# awk -F ":" '{ if($3>100){print $1}}' /etc/passwd systemd-network polkitd chrony [root@localhost ~]# awk -F ":" '{ if($3>100){print $1} else{print $1}}' /etc/passwd
算术运算符 运算符:+ - * / % ^
[root@localhost ~]# awk -F ":" '$3*$4 > 2000' /etc/passwd nobody:x:99:99:Nobody:/:/sbin/nologin systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin dbus:x:81:81:System message bus:/:/sbin/nologin polkitd:x:999:997:User for polkitd:/:/sbin/nologin postfix:x:89:89::/var/spool/postfix:/sbin/nologin sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin chrony:x:998:996::/var/lib/chrony:/sbin/nologin [root@localhost ~]# awk -F ":" '{if($3*10>500){print $1}}' /etc/passwd nobody systemd-network dbus polkitd postfix sshd chrony
逻辑操作符和复合模式 && 逻辑与 || 逻辑或 ! 逻辑非
[root@localhost ~]# awk -F ":" '{if($3>10 && $4<50){print $1}}' /etc/passwd operator [root@localhost ~]# awk -F ":" '$3>10 && $4<50{print $1}' /etc/passwd operator
范围模式 范围模式 先匹配从第一个模式的首次出现到第二个模式的首次出现之间的内容,如果匹配到第一个模式而没有发现第二个模式,awk就将显示从第一个模式首次出现的行到文件末尾之间的所有行。
[root@localhost ~]# awk '/Tom/,/John/' names Tom Savage 100 Molly Lee 200 John Doe 300
awk脚本编程 条件判断 if语句 格式:{if(表达式){语句1;语句2;……。}}
[root@localhost ~]# awk -F ":" '{if($3==0){print $1}}' /etc/passwd root [root@localhost ~]# awk -F ":" '{if($3>3 && $3<1000){print $1}}' /etc/passwd [root@localhost ~]# awk -F ":" '{if($3>0){print $1}else{print $7}}' /etc/passwd [root@localhost ~]# awk -F ":" '{if($3==0){count++}else{i++}}END{print count;print i}' /etc/passwd 1 18
if...else if...else语句 格式: {if(表达式1){语句1;语句2;……} else if(表达式2){语句1;语句2;……。}else{语句1;语句2;……。}} 循环语句 for语句 {for (expr1;expr2;expr3){statement}}
[root@localhost ~]# awk '/^[[:space:]]*kernel/{for(i=1;i<=NF;i++){print $1,length($i)}}' /etc/grub.conf kernel 6 kernel 30 kernel 2 kernel 46 kernel 10 kernel 15 kernel 11 kernel 8 kernel 16 kernel 16 kernel 9 kernel 8 kernel 4 kernel 5
统计出每个字段的长度。
[root@localhost ~]# awk -F ":" '/^root/{for(i=1;i<=NF;i++){print $1,length($i)}}' /etc/passwd root 4 root 1 root 1 root 1 root 4 root 5 root 9
[root@localhost ~]# awk -F ":" '/^root/{for(i=1;i<=NF;i++){print $i,length($i)}}' /etc/passwd root 4 x 1 0 1 0 1 root 4 /root 5 /bin/bash 9
while语句 格式,
{while (expression){statement}}
[root@localhost ~]# awk -F ":" '/root/{i=1;while(i<=NF){print $i,length($i);i++}}' /etc/passwd root 4 x 1 0 1 0 1 root 4 /root 5 /bin/bash 9 operator 8 x 1 11 2 0 1 operator 8 /root 5 /sbin/nologin 13
[root@localhost ~]# awk -F ":" '{i=1;while(i<=NF){if(length($i)>7){print $i,length($i)};i++}}' /etc/passwd
控制语句 break 跳出循环 continue 跳出本次循环,进行下一次循环 exit 终止awk程序
[root@localhost ~]# gawk -F ":" '{exit(1)}' /etc/passwd [root@localhost ~]# awk -F: '{if($1~/root/){next}else{print $0}}' /etc/passwd 如果某一行的第一个字段包含root,awk就路过该行,从输入文件读取下一行,然后从头开始执行脚本 [root@localhost ~]# awk '/^/dev|^UUID/{fs[$3]++}END{for(i in fs){print i,fs[i]}}' fs[$3]相同下标值加1 for i in fs把下标赋值给i fs[i]就是数组下标所存储的值。
swap 1
xfs 2
统计指定文件中每个单词出现的次数。
[root@localhost ~]# awk -F: '{for(i=1;i<=NF;i++){count[$i]++}}END{for(i in count){print i;count[i]}}' /etc/passwd
统计shell类型的个数
[root@localhost ~]# awk -F: '{shell[$NF]++}END{for(i in shell){print i,shell[i]}}' /etc/passwd /sbin/shutdown 1 /bin/bash 1 /sbin/nologin 24 /sbin/halt 1 /bin/sync 1
统计状态机
[root@localhost ~]# netstat -tan |grep :22|awk '{tcp_stats[$NF]++}END{for(i in tcp_stats){print i,tcp_stats[i]}}' LISTEN 2 ESTABLISHED 1 [root@localhost ~]# ss -tan|grep :22 |awk '{tcp_stats[$1]++}END{for(i in tcp_stats){print i,tcp_stats[i]}}' LISTEN 2 ESTAB 1
统计当前访问的每个ip数量
[root@localhost ~]# ss -tan|grep :22|awk -F: '{ips[$(NF-1)]++}END{for(i in ips){print i,ips[i]}}'|sort -rn -k2 22 192.168.1.5 1 22 * 1 1