awk其名称得自于它的创始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母。实际上 AWK 的确拥有自己的语言: AWK 程序设计语言 , 三位创建者已将它正式定义为“样式扫描和处理语言”。它允许您创建简短的程序,这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报表,还有无数其他的功能。
awk简明教程
简介
awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑(擅长取行),awk(擅长取列)在其对数据分析并生成报告时,显得尤为强大。简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。
awk有3个不同版本: awk、nawk和gawk,未作特别说明,一般指gawk,gawk 是 AWK 的 GNU 版本。
awk简单来讲:取文件中的某列数据。
通常,awk是以文件的一行为处理单位的。awk每接收文件的一行,然后执行相应的命令,来处理文本。
使用方法
1 2 3 4
|
awk '{pattern + action}' {filenames}
awk [可选的命令行选项] 'BEGIN{命令 } pattern{ 命令 } END{ 命令 }' 文件名
|
关于第二种方式,解释如下:
1.BEGIN 开头部分,可选的。用来设置一些参数,输出一些表头,定义一些变量等。上面的命令仅打印了一行信息而已。
2.END 结尾部分,可选的。用来计算一些汇总逻辑,或者输出这些内容。上面的命令,使用简单的for循环,输出了数组rt中的内容。
3.Pattern 匹配部分,依然可选。用来匹配一些需要处理的行。上面的命令,只匹配tcp开头的行,其他的不进入处理。
4.Action 模块。主要逻辑体,按行处理,统计打印,都可以。
初步使用
1.首先保存一个示例文档
1 2 3 4 5 6 7 8
|
$ cat netstat Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 2036/sshd tcp6 0 0 :::7500 :::* LISTEN 17767/./frps tcp6 0 0 :::80 :::* LISTEN 17767/./frps tcp6 0 0 :::7000 :::* LISTEN 17767/./frps tcp6 0 0 :::443 :::* LISTEN 17767/./frps
|
2.输出第一列和第四列
- 其中单引号中被大括号括着的就是awk语句,只能被单引号包含。
$1..$n表示第几列,$0表示整行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
$ awk '{print $1,$4}' netstat Active (only Proto Local tcp 0.0.0.0:22 tcp6 :::7500 tcp6 :::80 tcp6 :::7000 tcp6 :::443
$ cat /etc/passwd |awk -F ':' '{print $1}' root daemon bin sys
|
过滤记录
1.awk可以对其中的某些字段进行判断,通过与或非连接。awk 中的比较运算符用于比较字符串和或者数值,包括以下类型:
> – 大于
< – 小于
>= – 大于等于
<= – 小于等于
== – 等于
!= – 不等于
some_value ~ / pattern/ – 如果 some_value 匹配模式 pattern,则返回 true
some_value !~ / pattern/ – 如果 some_value 不匹配模式 pattern,则返回 true
如以下常用示例:
1 2 3 4 5 6 7 8 9 10 11 12
|
$ awk '$1=="tcp" && $6=="LISTEN" ' netstat tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 2036/sshd
$ awk '$1=="tcp" && $6=="LISTEN" || NR==2 ' netstat Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 2036/sshd
$ awk '$1=="tcp6" { print NR,$0 }' netstat 4 tcp6 0 0 :::7500 :::* LISTEN 17767/./frps 5 tcp6 0 0 :::80 :::* LISTEN 17767/./frps 6 tcp6 0 0 :::7000 :::* LISTEN 17767/./frps 7 tcp6 0 0 :::443 :::* LISTEN 17767/./frps
|
内建变量
程序已经定义好的变量,可以直接来使用,以下常见的变量。
| 变量 |
释义 |
$0 |
当前记录(这个变量中存放着整个行的内容) |
$1~$n |
当前记录的第n个字段,字段间由FS分隔 |
FS |
输入字段分隔符 默认是空格或Tab |
NF |
当前记录中的字段个数,就是有多少列 |
NR |
已经读出的记录数,就是行号,从1开始,如果有多个文件话,不断累加。 |
FNR |
当前记录数,与NR不同的是,这个值会是各个文件自己的行号 |
RS |
输入的记录分隔符, 默认为换行符 |
OFS |
输出字段分隔符, 默认也是空格 |
ORS |
输出的记录分隔符,默认为换行符 |
FILENAME |
当前输入文件的名字 |
❈ ❈ ❈ 一般的开发语言,数组下标是以0开始的,但awk的列$是以1开始的,而0指的是原始字符串。
1.输出表头、行号及特定列
1 2 3 4 5 6 7 8
|
$ awk '$3==0 && $6=="LISTEN" || NR==2 {print NR,FNR,$1,$3,$6}' netstat 2 2 Proto Send-Q Foreign 3 3 tcp 0 LISTEN 4 4 tcp6 0 LISTEN 5 5 tcp6 0 LISTEN 6 6 tcp6 0 LISTEN 7 7 tcp6 0 LISTEN
|
2.使用分隔符来输出特定的行或者列,下面👇两个命令等价:
1 2 3 4 5 6 7 8 9 10 11 12
|
$ awk 'BEGIN{FS=":"} {print $1, $7}' /etc/passwd | head -n 4 root /bin/bash bin /sbin/nologin daemon /sbin/nologin adm /sbin/nologin
$ cat /etc/passwd |awk -F ':' '{print $1"t"$7}' | head -n 4 root /bin/bash bin /sbin/nologin daemon /sbin/nologin adm /sbin/nologin
|
❈ ❈ ❈ 如果要是用多个分隔符,可以使用下面方式来指定:
3.自定义以t作为分隔符输出的例子:
1 2 3 4 5
|
$ awk -F: '{print $1,$7}' OFS="t" /etc/passwd | head -n 4 root /bin/bash bin /sbin/nologin daemon /sbin/nologin adm /sbin/nologin
|
字符串匹配
1.像grep一样匹配相关字符
1 2 3 4 5 6 7 8 9
|
$ awk '/80/' netstat tcp6 0 0 :::80 :::* LISTEN 17767/./frps
$ awk '/80|7000|tcp6/' netstat tcp6 0 0 :::7500 :::* LISTEN 17767/./frps tcp6 0 0 :::80 :::* LISTEN 17767/./frps tcp6 0 0 :::7000 :::* LISTEN 17767/./frps tcp6 0 0 :::443 :::* LISTEN 17767/./frps
|
2.精确的匹配某个字段
1 2 3 4 5 6 7
|
$ awk '$4 ~/80|7000/ {print $0}' netstat tcp6 0 0 :::80 :::* LISTEN 17767/./frps tcp6 0 0 :::7000 :::* LISTEN 17767/./frps
$ cat -n /etc/passwd | gawk -F: '/System/{print $1" "$5}' 17 dbus System message bus
|
❈ ❈ ❈ 其实 ~表示模式开始。/ /中是模式。这就是一个正则表达式的匹配。
对此模式取反的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
$ awk '!/80/' netstat Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 2036/sshd tcp6 0 0 :::7500 :::* LISTEN 17767/./frps tcp6 0 0 :::7000 :::* LISTEN 17767/./frps tcp6 0 0 :::443 :::* LISTEN 17767/./frps
$ awk '$4 !~/80|7000/ {print $0}' netstat Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 2036/sshd tcp6 0 0 :::7500 :::* LISTEN 17767/./frps tcp6 0 0 :::443 :::* LISTEN 17767/./frps
|
拆分文件
1.指定某列为分类符,使用重定向就可以导出到不同的文件中,例如下列语句通过第1列进行分割:
1 2 3 4 5 6 7
|
$ awk 'NR!=2{print > $1}' netstat $ cat tcp tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 2036/sshd $ ls netstat tcp tcp6
|
2.也可以把指定的列追加>>到文件,追加不会覆盖源文件,如下:
1 2 3 4
|
$ awk 'NR!=1{print $4,$7 >> $1}' netstat $ cat tcp tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 2036/sshd 0.0.0.0:22 2036/sshd
|
3.也可以使用复杂的表达式(例如if-else-if语句,awk是个脚本解释器)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
$ awk 'NR!=1{if($4 ~ /80|443/) print > "1.txt"; else if($4 ~/7000/) print > "2.txt"; else print > "3.txt" }' netstat $ ls ?.txt 1.txt 2.txt 3.txt $ cat 1.txt tcp6 0 0 :::80 :::* LISTEN 17767/./frps tcp6 0 0 :::443 :::* LISTEN 17767/./frps $ cat 2.txt tcp6 0 0 :::7000 :::* LISTEN 17767/./frps
$ cat 3.txt Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 2036/sshd tcp6 0 0 :::7500 :::* LISTEN 17767/./frps
|
统计
1.统计某个文件夹下的文件占用的字节数
1 2
|
$ ls -l |awk 'BEGIN {size=0;} {size=size+$5;} END{print "[end]size is ", size}' [end]size is 8657198
|
如果以M为单位显示:
1 2
|
$ ls -l |awk 'BEGIN {size=0;} {size=size+$5;} END{print "[end]size is ", size/1024/1024,"M"}' [end]size is 8.25889 M
|
注意,统计不包括文件夹的子目录。
2.统计以.txt为后缀的文件总大小:
1 2
|
$ ll *.txt | awk '{sum+=$5} END {print sum}' 769.9
|
3.统计文件夹下的文件数目:
1 2
|
$ ls -lha | gawk 'BEGIN {count=0} {count++} END { print "File Count:"count}' File Count:41
|
4.统计外网连接数,根据ip分组
1 2 3 4 5
|
$ netstat -ant | awk '/^tcp/{print $5}' | awk -F: '!/^:/{print $1}' | sort | uniq -c
$ sort 1.txt | uniq -c | sort -rn
|
5.统计第6列连接状态总共出现了几次
1 2 3
|
$ awk 'NR!=1{a[$6]++;} END {for (i in a) print i ", " a[i];}' netstat LISTEN, 5 Foreign, 1
|
6.统计每个用户的进程占用了多少内存
1 2 3 4 5
|
ps aux | awk 'NR!=1{a[$1]+=$6;} END {for(i in a) print i "," a[i]"KB"; }' dbus,1492KB polkitd,10972KB ntp,2104KB root,138580KB
|
awk脚本
在上面我们可以看到一个END关键字。END的意思是“处理完所有的行的标识”,即然说到了END就有必要介绍一下BEGIN,这两个关键字意味着执行前和执行后的意思,语法如下:
- BEGIN{ 这里面放的是执行前的语句 }
- END { 这里面放的是处理完所有的行后要执行的语句 }
- { 这里面放的是处理每一行时要执行的语句 }
so,举个栗子🌰
1 2 3 4 5 6
|
$ cat score Marry 2143 78 84 77 Jack 2321 66 78 45 Tom 2122 48 77 71 Mike 2537 87 97 95 Bob 2415 40 57 62
|
awk脚本如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
$ cat cal.awk
BEGIN { math = 0 english = 0 computer = 0 printf "NAME NO. MATH ENGLISH COMPUTER TOTALn" printf "---------------------------------------------n" }
{ math+=$3 english+=$4 computer+=$5 printf "%-6s %-6s %4d %8d %8d %8dn", $1, $2, $3,$4,$5, $3+$4+$5 }
END { printf "---------------------------------------------n" printf " TOTAL:%10d %8d %8d n", math, english, computer printf "AVERAGE:%10.2f %8.2f %8.2fn", math/NR, english/NR, computer/NR }
|
执行结果如下:
1 2 3 4 5 6 7 8 9 10 11 12
|
$ awk -f cal.awk score NAME NO. MATH ENGLISH COMPUTER TOTAL --------------------------------------------- Marry 2143 78 84 77 239 Jack 2321 66 78 45 189 Tom 2122 48 77 71 196 Mike 2537 87 97 95 279 Bob 2415 40 57 62 159 --------------------------------------------- TOTAL: 319 393 350 AVERAGE: 63.80 78.60 70.00
|
环境变量
awk脚本与环境变量的交互,使用-v参数(定义变量)和ENVIRON,使用ENVIRON的环境变量需要export。
1 2 3 4 5 6 7 8 9 10 11 12 13
|
$ x=5 $ y=10 $ echo $x 5 $ export y $ echo $x $y 5 10 $ awk -v val=$x '{print $1,$2,$3,$4+val,$5+ENVIRON["y"]}' OFS="t" score Marry 2143 78 89 87 Jack 2321 66 83 55 Tom 2122 48 82 81 Mike 2537 87 102 105 Bob 2415 40 62 72
|
有趣示例
1.从file文件中找出长度大于20的行
1 2 3 4 5 6
|
$ awk 'length>=21' score Marry 2143 78 84 77 Jack 2321 66 78 45 Tom 2122 48 77 71 Mike 2537 87 97 95 Bob 2415 40 57 62
|
2.按连接数查看客户端ip,根据ip分组计数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
$ netstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c 1 100.100.30.25 2 112.213.179.149 1 173.255.234.138 1 47.52.207.198 1 47.93.222.200 1 60.247.88.82 1 77.247.110.219 1 80.1.15.172 7 80.82.70.187 1 89.248.174.198 1 Address 1 servers)
|
3.打印九九乘法表
1 2 3 4 5 6 7 8 9 10
|
$ seq 9 | sed 'H;g' | awk -v RS='' '{for(i=1;i<=NF;i++)printf("%dx%d=%d%s", i, NR, i*NR, i==NR?"n":"t")}' 1x1=1 1x2=2 2x2=4 1x3=3 2x3=6 3x3=9 1x4=4 2x4=8 3x4=12 4x4=16 1x5=5 2x5=10 3x5=15 4x5=20 5x5=25 1x6=6 2x6=12 3x6=18 4x6=24 5x6=30 6x6=36 1x7=7 2x7=14 3x7=21 4x7=28 5x7=35 6x7=42 7x7=49 1x8=8 2x8=16 3x8=24 4x8=32 5x8=40 6x8=48 7x8=56 8x8=64 1x9=9 2x9=18 3x9=27 4x9=36 5x9=45 6x9=54 7x9=63 8x9=72 9x9=81
|
参考链接:
AWK 简明教程
近期评论