处理文本文件,进行文本分析
REFERENCE:
The Syntax of awk
awk [选项参数] 'script' var=value file(s)
或
awk [选项参数] -f scriptfile var=value file(s)
选项参数
-
-F fs
|--fiel-separator fs
指定输入文件折分隔符,fs是一个字符串或者是一个正则表达式,如`-F:。 -
-v var=value
|--asign var=value
赋值一个用户定义变量。awk -F: -v i=5 '{ print $3,$(i-2) }' /etc/passwd 0 0 1 1 2 2
-
-f scripfile
|--file scriptfile
从脚本文件中读取awk命令。awk -f xxx.awk /etc/passwd
-
-mf nnn
&-mr nnn
对nnn值设置内在限制,-mf
选项限制分配给nnn的最大块数目;-mr
选项限制记录的最大数目。这两个功能是Bell实验室版awk的扩展功能,在标准awk中不适用。 -
-W compact
|--compat
,-W traditional
|--traditional
在兼容模式下运行awk。所以gawk的行为和标准的awk完全一样,所有的awk扩展都被忽略。 -
-W copyleft
|--copyleft
,-W copyright
|--copyright
打印简短的版权信息。 -
-W help
|--help
,-W usage
|--usage
打印全部awk选项和每个选项的简短说明。 -
-W lint
|--lint
打印不能向传统 unix 平台移植的结构的警告。 -
-W lint-old
|--lint-old
打印关于不能向传统unix平台移植的结构的警告。 -
-W posix
打开兼容模式。但有以下限制,不识别:/x、函数关键字、func、换码序列以及当 fs 是一个空格时,将新行作为一个域分隔符;操作符**
和**=
不能代替^
和^=
;fflush无效。 -
-W re-interval
|--re-inerval
允许间隔正则表达式的使用,参考(grep中的Posix字符类),如括号表达式 [[:alpha:]]。 -
-W source program-text
|--source program-text
使用 program-text 作为源代码,可与 -f 命令混用。 -
-W version
|--version
打印 bug 报告信息的版本。
AWK 原理
只查看 passwd 文件内,第 20 到 30 行的内容
awk '{ if( NR>=20 && NR<=30 ){print $0} }' /etc/passwd
mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/false
nginx:x:998:996:nginx user:/var/cache/nginx:/sbin/nologin
已知 passwd 文件内容,从中过滤出用户名 root 和命令解析器 /bin/bash,最后输出 root /bin/bash
awk -F ':' '{ if( NR==1 )print $1" "$7 }' /etc/passwd
root /bin/bash
BEGIN/END 模块
统计 /etc/passwd 的账户人数
awk '{count++} END{print "[END] The number of users is ",count}' /etc/passwd
[END] The number of users is 21
count 是自定义变量,这里没有初始化 count,虽然默认是 0,但最稳妥的做法还是初始化
awk 'BEGIN{count=0} {count++} END{print "[END] The number of users is ",count}' /etc/passwd
[END] The number of users is 21
AWK 运算符
Description | Operational Character |
---|---|
赋值 | = += -= *= /= %= ^= **= |
逻辑 | || && |
正则 | ~ 匹配正则表达式; !~ 不匹配正则表达式 |
关系 | < > <= >= != == |
算术 | + - * / & 取余;^ *** 求幂;++ – |
其他 | In 数组中是否存在某键值;$ 字段引用 |
赋值
awk 'BEGIN{ a=5;a+=5;print a }'
10
逻辑
awk 'BEGIN{ a=0;print ( a>-1||a<0 , a>-1&&a<0 ) }'
1 0
正则
awk 'BEGIN{ str="192,168,10,222";if( str~10 ){print "true"} }'
true
echo | awk 'BEGIN{ str="192,168,10,222" } str~10 {print "true"}'
true
关系
awk 'BEGIN{ a=0;print (a<0,a==0,a>0) }'
0 1 0
<
>
可以比较字符串,也可比较数值。
awk 'BEGIN{ a="11";if(a>=9){print "true"} }' # 无输出,比较的 ASCII 码顺序
awk 'BEGIN{ a=11;if(a>=9){print "true"} }'
true
算术
经算数运算符操作,操作数自动转为数值,所有非数值都变为0
awk 'BEGIN{ a="b";b="2b";print a,b,a++,b++ }'
b 2b 0 2
其他:三目运算
awk 'BEGIN{ a="b";print a=="b"?1:0 }'
1
AWK 内置变量
Variate | Description | Default | |
---|---|---|---|
$0 | 当前记录 | ||
$1~$n | 当前记录的第 n 个字段 | ||
FS | Field Separator | 输入字段分隔符 | 空格 |
RS | Record Separator | 输入记录分隔符 | \t |
NF | Number Of Field | 当前记录中的字段个数;总列数 | |
NR | Number Of Record | 当前记录数;行号 | |
OFS | Output Field Separator | 输出字段分隔符 | 空格 |
ORS | Output Record Separator | 输出记录分割符 | \t |
FS 字段分割符
换行
awk 'BEGIN{ FS="\t+" }{ print $0 }' xxx.md # 一个 or 多个 Tab 分割符
空格
awk -F [[:space:]+] '{ print $0 }' xxx.md # 一个 or 多个空格
多个分隔符
awk -F '[ :/]' 'BEGIN{ OFS="\t" }{ print $2,$3,$9 }' /etc/passwd
x 0 bin
x 1 sbin
x 2 sbin
RS 记录分隔符 ⭐️
awk 'BEGIN{ RS="" }{ print $0 }' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
·················
mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/false
nginx:x:998:996:nginx user:/var/cache/nginx:/sbin/nologin
awk 'BEGIN{ RS="" }{ print $1 }' /etc/passwd
root:x:0:0:root:/root:/bin/bash
awk 'BEGIN{ RS="" }{ print $2 }' /etc/passwd
bin:x:1:1:bin:/bin:/sbin/nologin
NF 字段数量
awk -F "/" 'NF==5{print $0}' /etc/passwd # 按 / 分割,字段数量为 5 的打印
adm:x:3:4:adm:/var/adm:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
ntp:x:38:38::/etc/ntp:/sbin/nologin
NR 记录数量
awk 'NR==1{print $0}' /etc/passwd # NR==1,取第一行数据
root:x:0:0:root:/root:/bin/bash
OFS 输出字段分隔符
略
ORS 输出记录分隔符
略
IGNORECASE 忽略大小写
awk 'BEGIN{ IGNORECASE=1 } /user/' /etc/passwd
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
nginx:x:998:996:nginx user:/var/cache/nginx:/sbin/nologin
AWK 正则表达
Character | Function | Samples | Interpretation |
---|---|---|---|
^ | 行首定位符 | /^root / | 匹配所有以 root 开头的行 |
$ | 行尾定位符 | /root$ / | 匹配所有以 root 结尾的行 |
. | 匹配 1 个字符 | /r..t / | 匹配 r 为头,t 为尾的四字符串 |
* | 匹配 [0,+∞) 个前导字符 | /roo*t / | |
+ | 匹配 [1,+∞) 个前导字符 | /ro+t / | |
? | 匹配 [0,1] 个前导字符 | /r?oot / | |
[] | 匹配 [] 内的任意一个字符 | /^[abc] / | 匹配以 a、b、c 开头的行 |
[^] | 匹配不属于 [^] 内的字符 | /^[^ab] / | 匹配不以 a、b 开头的行 |
() | 子表达式组合 | /(root)+ / | [1,+∞) 个 root 组合 |
| | 或者 | /(root)|(user) / | 匹配 root 或 user 的行 |
\ | 转义字符 | /a\ / | 匹配 a/ |
~ | 匹配 | $1~ /root / | 匹配第一个字段包含字符 root 的行 |
!~ | 不匹配 | $1!~ /root / | |
x{m} | x 重复 m 次 | /[rot]{4} / | 匹配四个连续字符全由 rot 组成的行 |
x{m,} | x 重复 m 次及以上 | ||
x{m,n} | x 重复 m~n 次 |
规则表达式
awk '/REG/{ACTION}' FILE # /REG/ 为正则表达式,可以将 $0 中,满足条件的的记录送到 ACTION 进行处理
awk '/root/{print $0}' /etc/passwd # 匹配包含 root 的行
布尔表达式
awk 'BOOLEAN{ACTION}' FILE # 仅当 BOOLEAN 值为 TRUE 时,awk 才执行 ACTION
awk -F: '$1=="root"{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
条件循环
if
if($1=="root"){
print $0
}
while
do while
count=1
do{
print $1
} while( count !=1 )
for
for( i=1;i<10;i++){
print $1
}
数组
用 awk 中查看服务器连接状态并汇总
netstat -an|awk '/^tcp/{++s[$NF]}END{for(a in s)print a,s[a]}'
ESTABLISHED 1
LISTEN 20
统计 web 日志访问流量,要求输出访问次数,请求页面或图片,每个请求的总大小,总访问流量的大小汇总
awk '{a[$7]+=$10;++b[$7];total+=$10}END{for(x in a)print b[x],x,a[x]|"sort -rn -k1";print "total size is :"total}' /app/log/access_log
total size is :172230
21 /icons/poweredby.png 83076
14 / 70546
8 /icons/apache_pb.gif 18608
a[$7]+=$10 表示以第 7 列为下标的数组( $10 列为$7 列的大小),把他们大小累加得到 $7 每次访问的大小,后面的 for 循环有个取巧的地方, a 和 b 数组的下标相同,所以一条 for 语句足矣
字符串函数
Function | Description |
---|---|
gsub( Ere,Repl,[In] ) | |
sub( Ere,Repl,[In] ) | |
index( String1,String2 ) | |
length[( String )] | 字符串长度 |
blength[( String )] | 字符串长度,单位字节 |
substr( String,M,[N] ) | 字符串截取 |
match( String,Ere ) | |
split( String,A,[Ere] ) | |
tolower( String ) | |
toupper( String ) | |
sprintf( Format,Expr,Expr,... ) |
gsub 替换
awk 'BEGIN{ str="abc123abc";gsub(/[0-9]+/,"!",str);print str }'
abc!abc
在 str 中查找满足正则表达式的子串,用 !
替换,并返回替换后的值给 str
index 查找
awk 'BEGIN{ str="abc123abc";print index(str,"abc")?"true":"false" }'
true # 查找到则返回非零
match 匹配查找
awk 'BEGIN{ str="abc123abc";print match(str,/[0-9]+/) }'
4
substr 截取
awk 'BEGIN{ str="abc123abc";print substr(str,4,6) }'
123abc
Exercise
格式化输出
awk -F: '{printf "%-8s %-10s\n",$1,$6 }' /etc/passwd
root /root
bin /bin
daemon /sbin
运算符:过滤第三列小于 3 的行
awk -F: '$3<3' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
计算文件大小
ls -l | awk '{ sum+=$5 } END{ print sum }'
1535
从文件中找出长度大于 60 的行
awk 'length>60' /etc/passwd
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
打印九九乘法表
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