技.艺.道:查漏补缺之-awk和sort

一、AWK

1.简介

AWK是一个优良的文本处理工具,Linux及Unix环境中现有的功能最强大的数据处理引擎之一。这种编程及数据操作语言(其名称得自于它的创始人阿尔佛雷德·艾侯、彼得·温伯格和布莱恩·柯林汉姓氏的首个字母)的最大功能取决于一个人所拥有的知识,很多事情往往都是这样。

我们可以在linux环境中使用它来进行快速的、任意规模的数据统计分析。大到几十G、小到随手统计文件信息均可。

2.使用

示例数据:log.txt
1,aa a,ccc,aw qr
2,ss ss,d dd,e e e,ll l
3,0 0,98 u,kj lh,ll x,2 23,hh 
4,23 3,s ss

1)取出指定列(默认分隔符为 空格:“ ”):awk '{print $1,$4}' log.txt

$x表示第x列。

root@ubuntu:/leonRain/test# awk '{print $1,$4}' log.txt
1 awqr
2 eee
3 kjlh
4 

2)取出指定列并格式化输出:awk '{printf "%-3s %-18s %-3s\n",$1,$4,$1}' log.txt

{printf "%-3s %-18s %-3s\n",$1,$4,$1}:第1列与第4列隔3个空格,第4列与再一个第1列隔18个空格,最后加3个空格

root@ubuntu:/leonRain/test# awk '{printf "%-3s %-18s %-3s\n",$1,$4,$1}' log.txt
1   awqr               1  
2   eee                2  
3   kjlh               3  
4                      4  

x)使用新示例数据log2.txt:

root@ubuntu:/leonRain/test# cat log2.txt 
1,aa a,ccc,aw qr
2,ss ss,d dd,e e e,ll l
3,0 0,98 u,kj lh,ll x,2 23,hh 
4,23 3,s ss

3)指定分隔符:awk -F, '{print $1,$2}' log2.txt

“-F,”:以","为分隔符

使用内建变量:awk 'BEGIN{FS=","} {print $1,$2}' log.txt

root@ubuntu:/leonRain/test# awk -F, '{print $1,$2}' log2.txt
1 aa a
2 ss ss
3 0 0
4 23 3

root@ubuntu:/leonRain/test# awk 'BEGIN{FS=","} {print $1,$2}' log2.txt
1 aa a
2 ss ss
3 0 0
4 23 3

4)使用多个分隔符,先使用空格分割,然后对分割结果再使用","分割。最后的分割结果在同一层。

 $ awk -F '[ ,]' '{print $1,$2,$5}' log2.txt

root@ubuntu:/leonRain/test# awk -F '[ ,]' '{print $1,$2,$5}' log2.txt
1 aa aw
2 ss dd
3 0 u
4 23 ss

5)设置 数值型 变量:(例如:设置a=1,b=8)

awk -va=1 -vb=8 '{print $1,$1+a,$1*b}' log2.txt

root@ubuntu:/leonRain/test# awk -va=1 -vb=8 '{print $1,$1+a,$1*b}' log2.txt
1,aa 2 8
2,ss 3 16
3,0 4 24
4,23 5 32

6)设置 字符(串)型 变量(例如:设置a=1,b=s)

awk -va=1 -vb=s '{print $1,$1+a,$1b}' log2.txt

root@ubuntu:/leonRain/test# awk -va=1 -vb=s '{print $1,$1+a,$1b}' log2.txt
1,aa 2 1,aas
2,ss 3 2,sss
3,0 4 3,0s
4,23 5 4,23s

7)执行awk脚本:awk -f {awk脚本} {文件名}

实例: $ awk -f cal.awk log.txt

(编写awk脚本稍后介绍。)

8)逻辑运算:单引号中可以使用使用 条件表达式 实现过滤。

awk '$1>2' log2.txt

root@ubuntu:/leonRain/test# awk '$1>2' log2.txt
2,ss ss,d dd,e e e,ll l
3,0 0,98 u,kj lh,ll x,2 23,hh 
4,23 3,s ss

9)单引号中还可以加入{print …}来指定要打印出符合条件的数据的哪些列。

awk '$1>3 {print $1,$2,$3}' log2.txt

root@ubuntu:/leonRain/test# awk '$1>3 {print $1,$2,$3}' log2.txt
3,0 0,98 u,kj
4,23 3,s ss

10)多条件查询,找到第一列大于3,且第三列等于“ss”的。

awk '$1>3 && $3=="ss" {print $1,$2,$3}' log2.txt

root@ubuntu:/leonRain/test# awk '$1>3 && $3=="ss" {print $1,$2,$3}' log2.txt
4,23 3,s ss

11)输出内置函数结果值

示例数据3:

root@ubuntu:/leonRain/test# cat log3.txt 
11,222,333,444,555
12,222,332,111,444
13,123,1243,999,4
14,25,12,333,1
15,99,99,99,99
16,22,22,22,44
17,11,11,11,11

 查看这些数据的所在文件名,命令行参数的数目。。。等内容(可查表得知这些代码对应的内置函数功能,表格在附录2)

awk -F, 'BEGIN{printf "%4s %4s %4s %4s %4s %4s %4s %4s %4s\n","FILENAME","ARGC","FNR","FS","NF","NR","OFS","ORS","RS";printf "---------------------------------------------\n"} {printf "%4s %4s %4s %4s %4s %4s %4s %4s %4s\n",FILENAME,ARGC,FNR,FS,NF,NR,OFS,ORS,RS}' log3.txt

root@ubuntu:/leonRain/test# awk -F, 'BEGIN{printf "%4s %4s %4s %4s %4s %4s %4s %4s %4s\n","FILENAME","ARGC","FNR","FS","NF","NR","OFS","ORS","RS";printf "---------------------------------------------\n"} {printf "%4s %4s %4s %4s %4s %4s %4s %4s %4s\n",FILENAME,ARGC,FNR,FS,NF,NR,OFS,ORS,RS}' log3.txt
FILENAME ARGC  FNR   FS   NF   NR  OFS  ORS   RS
---------------------------------------------
log3.txt    2    1    ,    5    1         
    

log3.txt    2    2    ,    5    2         
    

log3.txt    2    3    ,    5    3         
    

log3.txt    2    4    ,    5    4         
    

log3.txt    2    5    ,    5    5         
    

log3.txt    2    6    ,    5    6         
    

log3.txt    2    7    ,    5    7         
    

黄色部分打印表头,绿色部分是真正的定制结果,即格式化输出:“FILENAME,ARGC,FNR,FS,NF,NR,OFS,ORS,RS”这些内容。

12)亦可直接在 单引号中使用

awk '{print NR,FNR,$1,$2,$3}' log3.txt

root@ubuntu:/leonRain/test# awk '{print NR,FNR,$1,$2,$3}' log.txt
1 1 1 aaa ccc
2 2 2 ssss ddd
3 3 3 00 98u
4 4 4 233 ssSSs

13)指定输出时的字段分隔符

awk '{print $1,$2,$5}' OFS=" $ "  log3.txt

root@ubuntu:/leonRain/test# awk -F, '{print $1,$2,$5}' OFS=" $ "  log3.txt
11 $  222 $  555
12 $  222 $  444
13 $  123 $  4
14 $  25 $  1
15 $  99 $  99
16 $  22 $  44
17 $  11 $  11

14)通过正则表达式匹配,查找第二列中包含"ab"的数据,并输出其第二和第四列。

awk '$2 ~ /a/ {print $2,$4}' log.txt

root@ubuntu:/leonRain/test# awk '$2 ~ /a/ {print $2,$4}' log.txt
aaa awqr

若指定在某列中匹配,语法为:'$列号 ~ /匹配条件/' 例如:awk '$2 ~ /s/' log2.txt

若在整条数据中匹配,则不需要使用"~",语法为:awk '/s/' log2.txt

awk '$2 ~ /s/ && $3 ~ /s/' log2.txt

root@ubuntu:/leonRain/test# awk '$2 ~ /s/ && $3 ~ /s/' log2.txt
4,23 3,s ss

xx)使用新的示例数据:

root@ubuntu:/leonRain/test# cat logstr.txt 
this is a dog;
This is a dog;
Are you ok?
are you OK?

15)模式取反,即利用感叹号“!”进行条件取反

awk '$1 !~ /this/' logstr.txt 

root@ubuntu:/leonRain/test# awk '$1 !~ /this/' logstr.txt 
This is a dog;
Are you ok?
are you OK?

16)awk脚本编写

关于 awk 脚本,我们需要注意两个关键词 BEGIN 和 END。

  • BEGIN{ 这里面放的是执行前的语句 }
  • END {这里面放的是处理完所有的行后要执行的语句 }
  • {这里面放的是处理每一行时要执行的语句

样例(取自菜鸟教程,我加了一些注释):

示例数据(学生成绩表):
	$ cat score.txt
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 脚本如下:

$ cat cal.awk
#!/bin/awk -f
# 运行前 定义变量,打印表头
BEGIN {
    math = 0
    english = 0
    computer = 0
 
    printf "NAME    NO.   MATH  ENGLISH  COMPUTER   TOTAL\n"
    printf "---------------------------------------------\n"
}
# 运行中 规定遍历数据时的具体操作。这里进行的是变量与具体列的逐行累加,
# 同时输出该行数据及一些计算值,如这里最后输出了一个总分 $3+$4+$5。
{
    math+=$3
    english+=$4
    computer+=$5
    printf "%-6s %-6s %4d %8d %8d %8d\n", $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.2f\n", math/NR, english/NR, computer/NR
}

执行结果:

$ awk -f cal.awk score.txt
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

17)拓展

统计文件大小:

ls -l *.txt | awk '{sum+=$5} END {print sum}'

这段命令分为两部分:“ls -l *.txt ”作用是查看当前所在路径下的.txt后缀的文件信息。

“awk '{sum+=$5} END {print sum}'”作用是对出入数据的第五列进行统计,并在结束时输出结果。

root@ubuntu:/leonRain/test# ll
total 28
drwxr-xr-x  2 root root 4096 May 27 16:20 ./
drwxr-xr-x 20 root root 4096 May 27 13:55 ../
-rw-r--r--  1 root root   84 May 27 14:41 log2.txt
-rw-r--r--  1 root root  116 May 27 15:43 log3.txt
-rw-r--r--  1 root root   54 May 27 16:20 logstr.txt
-rw-r--r--  1 root root   72 May 27 16:09 log.txt
-rwxr-xr-x  1 root root   38 May 27 14:56 tt.awk*

root@ubuntu:/leonRain/test# ls -l *.txt | awk '{sum+=$5} END {print sum}'
326

找到当前目录中属于root用户的文件:

ls -l * | awk '$4 ~ /root/'

root@ubuntu:/leonRain/test# ll
total 28
drwxr-xr-x  2 root root 4096 May 27 16:20 ./
drwxr-xr-x 20 root root 4096 May 27 13:55 ../
-rw-r--r--  1 root root   84 May 27 14:41 log2.txt
-rw-r--r--  1 root root  116 May 27 15:43 log3.txt
-rw-r--r--  1 root root   54 May 27 16:20 logstr.txt
-rw-r--r--  1 root root   72 May 27 16:09 log.txt
-rwxr-xr-x  1 root root   38 May 27 14:56 tt.awk*

root@ubuntu:/leonRain/test# ls -l * | awk '$4 ~ /root/'
-rw-r--r-- 1 root root  84 May 27 14:41 log2.txt
-rw-r--r-- 1 root root 116 May 27 15:43 log3.txt
-rw-r--r-- 1 root root  54 May 27 16:20 logstr.txt
-rw-r--r-- 1 root root  72 May 27 16:09 log.txt
-rwxr-xr-x 1 root root  38 May 27 14:56 tt.awk

我们可以利用上面这种方法,将常用的linux命令与awk结合使用,即:将常用linux命令的输出结果作为awk的输入数据,对结果进行过滤、统计等常规简易操作。

附录1.awk逻辑运算说明表

运算符

描述

= += -= *= /= %=(取模赋值) ^=(异或赋值) **=(幂赋值)

赋值

?:

C条件表达式

||

逻辑或

&&

逻辑与

~ 和 !~

匹配正则表达式和不匹配正则表达式

< <= > >= != ==

关系运算符

空格

连接

+ -

加,减

* / %

乘,除与求余

+ - !

一元加,减和逻辑非

^ ***

求幂

++ --

增加或减少,作为前缀或后缀

$

字段引用

in

数组成员

附录2.awk内建变量表

变量

描述

$n

当前记录的第n个字段,字段间由FS分隔

$0

完整的输入记录

ARGC

命令行参数的数目

ARGIND

命令行中当前文件的位置(从0开始算)

ARGV

包含命令行参数的数组

CONVFMT

数字转换格式(默认值为%.6g)ENVIRON环境变量关联数组

ERRNO

最后一个系统错误的描述

FIELDWIDTHS

字段宽度列表(用空格键分隔)

FILENAME

当前文件名

FNR

各文件分别计数的行号

FS

字段分隔符(默认是任何空格)

IGNORECASE

如果为真,则进行忽略大小写的匹配

NF

一条记录的字段的数目

NR

已经读出的记录数,就是行号,从1开始

OFMT

数字的输出格式(默认值是%.6g)

OFS

输出记录分隔符(输出换行符),输出时用指定的符号代替换行符

ORS

输出记录分隔符(默认值是一个换行符)

RLENGTH

由match函数所匹配的字符串的长度

RS

记录分隔符(默认是一个换行符)

RSTART

由match函数所匹配的字符串的第一个位置

SUBSEP

数组下标分隔符(默认值是/034)

 

二、sort

1.简介

排序操作也是经常使用的,它可以通过sort命令实现。

2.示例

语法:sort [-bcdfimMnr][-o<输出文件>][-t<分隔字符>][+<起始栏位>-<结束栏位>][--help][--verison][文件]

sort [-b 或 c 或 d…r均可][-o<输出文件>][-t<分隔字符>][+<起始栏位>-<结束栏位>][--help][--verison][文件]

1)对当前目录所有文件按大小排序。(第五列表示文件大小,因此用-k5,默认是正序排列,若需倒序可使用-rk5)

ll * | sort -n -k5
-rwxr-xr-x 1 root root  38 May 27 14:56 tt.awk*
-rw-r--r-- 1 root root  54 May 27 16:20 logstr.txt
-rw-r--r-- 1 root root  72 May 27 16:09 log.txt
-rw-r--r-- 1 root root  84 May 27 14:41 log2.txt
-rw-r--r-- 1 root root 116 May 27 15:43 log3.txt

2)对当前目录所有文件按修改时间排序。

ll * | sort -n -k8
-rw-r--r-- 1 root root  84 May 27 14:41 log2.txt
-rwxr-xr-x 1 root root  38 May 27 14:56 tt.awk*
-rw-r--r-- 1 root root 116 May 27 15:43 log3.txt
-rw-r--r-- 1 root root  54 May 27 16:20 logstr.txt
-rw-r--r-- 1 root root  72 May 27 16:09 log.txt

3)sort还可以用来处理文件。

查看原始数据文件:

root@ubuntu:/leonRain/test# cat log.txt
1 aaa ccc awqr
2 ssss ddd eee lll
3 00 98u kjlh llx 223 hh
4 233 ssSSs

使用sort对其进行默认排序:

root@ubuntu:/leonRain/test# sort log.txt
1 aaa ccc awqr
2 ssss ddd eee lll
3 00 98u kjlh llx 223 hh
4 233 ssSSs

使用sort对其进行倒序排序:

root@ubuntu:/leonRain/test# sort -r log.txt
4 233 ssSSs
3 00 98u kjlh llx 223 hh
2 ssss ddd eee lll
1 aaa ccc awqr

使用sort按其第二列进行倒序排序:

root@ubuntu:/leonRain/test# sort -rk2 log.txt
2 ssss ddd eee lll
1 aaa ccc awqr
4 233 ssSSs
3 00 98u kjlh llx 223 hh

 

参数说明:

-b 忽略每行前面开始出的空格字符。

-c 检查文件是否已经按照顺序排序。

-d 排序时,处理英文字母、数字及空格字符外,忽略其他的字符。

-f 排序时,将小写字母视为大写字母。

-i 排序时,除了040至176之间的ASCII字符外,忽略其他的字符。

-m 将几个排序好的文件进行合并。

-M 将前面3个字母依照月份的缩写进行排序。

-n 依照数值的大小排序。

-u 意味着是唯一的(unique),输出的结果是去完重了的。

-o<输出文件> 将排序后的结果存入指定的文件。

-r 以相反的顺序来排序。

-t<分隔字符> 指定排序时所用的栏位分隔字符。

+<起始栏位>-<结束栏位> 以指定的栏位来排序,范围由起始栏位到结束栏位的前一栏位。

--help 显示帮助。

--version 显示版本信息。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章