文本处理命令awk 实例学习参考
awk.jpeg
文章源地址
awk命令是Linux系统里非常强大和功能丰富的文本行处理工具命令,使用方法简单,但是功能丰富,处理文本的速度也非常快。awk是Linux及Unix环境中现有的功能最强大的数据处理引擎之一。强大到一个命令工具拥有了自己的语言,Awk的名字来源于它的三个开发者首字母(Alfred Aho, Peter Weinberger, and Brian Kernighan), 这款软件在1977年就有了,一直保留至今足以说明其重要性了。当你遇到文本处理问题一筹莫展时,而你恰恰又看到了awk命令的使用方法介绍,那么你会在这里找到你的解决方法的。
awk语法
awk的基本使用方法如下:
pattern { action }
pattern
就是模式,用来精准的查找到你要处理的数据位置,支持数值判断,字符串比较,正则匹配等。
action
就是要对 pattern
查找到的数据行进行处理的逻辑动作,例如输出找到的行数据、统计行数或者是查找是否有你的名字等等。
awk pattern语法
模式pattern
可以是以下任何任何一种格式:
BEGIN : 内建模式,表示在处理文件之前的模式操作,我们可以在这个模式中执行一些初始化等预先的动作任务。 END : 内建模式,表示在处理文件之后的模式操作,我们可以在这个模式中执行一些展示统计结果等输出的动作任务。 /regular expression/ : 正则表达式,支持大部分正则语法,例如 /^[0-9]/ 匹配首部包含数字的行。 relational expression : 关系表达式,例如 大于,小于,等于的比较 `$1 > 100` 匹配 第一个字段大于100的行。 pattern && pattern : 模式与模式的逻辑与,两模式都真时匹配行。 pattern || pattern : 模式与模式的逻辑或,两模式至少一个为真即可匹配行。 ! pattern : 模式的逻辑非, 当模式为假时匹配行, 例如 `! $1 > 10` 匹配的是第一个字段不大于10的行数据。 (pattern) : 括号包含的模式,让模式表达的逻辑关系可以更复杂,例如 `($1~/^ABC/ && $2 > 100) || $1~/^XYZ/` ,匹配 第一列为"ABC"并且第二列大于100的行或者匹配第一列以XYZ开头的行,其实可以更复杂,不过太复杂就不太直观理解,能简单化尽量简单表达。 pattern1, pattern2 : 范围模式匹配,匹配 `pattern1`和`pattern2`之间的行数据。 pattern1 ? pattern2 : pattern3 : 三元运算,这个在Unix系统有可能不支持的,在 `gawk`和`nawk`下通常是支持的, 如果 `pattern1` 为真,则当 `pattern2`为真的行数据匹配成功,如果 `pattern1`为假,则当` pattern3` 为真时匹配行数据, 例如 `$1~/^ABC/ ? $2 > 100 : $1~/^XYZ/` ,预期类似的语法为`($1~/^ABC/ && $2 > 100) || $1~/^XYZ/` ,所以这个是可以通过其他逻辑表达的,很少用到了。
awk 内建变量
NR:已输入记录的条数。 NF:当前记录中域的个数。记录中最后一个域可以以$NF的方式引用。 FILENAME:当前输入文件的文件名。 FS:“域分隔符”,用于将输入记录分割成域。其默认值为“空白字符”,即空格和制表符。FS可以替换为其它字符,从而改变域分隔符。 RS:当前的“记录分隔符”。默认状态下,输入的每行都被作为一个记录,因此默认记录分隔符是换行符。 OFS:“输出域分隔符”,即分隔print命令的参数的符号。其默认值为空格。 ORS:“输出记录分隔符”,即每个print命令之间的符号。其默认值为换行符。 OFMT:“输出数字格式”(Format for numeric output),其默认值为"%.6g"。
awk 内建函数
awk 脚本用法
以一个
hello.awk
脚本文件为例,说明使用awk脚本方法
- 在
awk
脚本第一行添加#!/usr/bin/awk -f
,表示这个脚本需要使用 awk 命令进行解析执行; - 创建脚本后,还需要给脚本文件增加
可执行权限
,命令为chmod +x ./hello.awk
。
增加完可执行权限
后,我们就可以像执行命令一样执行hello.awk
脚本了。
$ chmod +x hello.awk $ ./hello.awk Hello, world! $ cat hello.awk #!/usr/bin/awk -f BEGIN { print "Hello, world!" }
如果我们hello.awk
给脚本命名是 cat
的话,这与系统自带的 cat
命令重名了,那么我们怎么知道这个是不是系统命令呢?
$ file ./cat cat: awk script, ASCII text executable
当然,我们可以直接查看这个cat
文件内容就可以知道它是一个awk
脚本了,然而对于二进制文件
的时候file
命令就非常有帮助啦。
awk 实例
awk 第一个实例
第一个是Hello World!
:
$ echo "Bob"|awk 'BEGIN{ print "Hello "}{ print $0} END{ print "World!"}' Hello Bob World!
-
BEGIN
模式是在处理所有数据之前一定要匹配的模式 -
END
模式是在处理完毕所有数据之后一定要匹配的模式(常常用END进行数据展示输出)。 - 中间的
{print $0}
没有写任何模式,默认会处理所有行数据。
awk 第二个实例:模式的使用
awk '$1~/^[0-9]/ && NF > 1 { total_result += $1 } END{ printf("result: %.0f\n", total_result); }' a.txt
awk 第三个实例:内建变量使用
awk -F'[, ]' 'BEGIN{ ## 设置输出域分割符号为冒号: OFS=":" }/^[0-9]/ && $1 > 1000{ print FNR,NR,NF,FILENAME }' b.txt a.txt 3:3:3:b.txt 4:4:2:b.txt 5:5:3:b.txt 6:6:3:b.txt 1:14:10:a.txt 2:15:10:a.txt 3:16:10:a.txt 4:17:10:a.txt 5:18:10:a.txt
- 在
BEGIN
模式中设置了输出域分隔符
变量OFS
为“:”,之后使用print
的默认域分隔符都使用OFS
。 - 处理文件为两个(a.sh 和 a.txt ) , 此时对比
FNR
和NR
的差异了,NF
有时候也会用来格式化过滤一些垃圾数据。
awk 第四个实例: 逻辑判断和循环控制
下面是一个统计英文单词频率的实例,使用两种for
循环语法
$ cat vuln.c #include<stdio.h> int main(int argc, char *argv[]) { char buffer[500]; strcpy(buffer, argv[1]); return 0; } $ awk -F"[^a-zA-Z]+" '{ for(i=1; i<=NF; ++i) { words[tolower($i)]++ } } END { for(i in words) { if( words[i]) { print i, words[i] } } }' vuln.c argv 2 char 2 h 1 13 strcpy 1 buffer 2 main 1 argc 1 return 1 stdio 1 include 1 int 2
-
for
循环的语法中,我比较常用的是for( i in words)
这种, 利用哈希(hash
)进行数据存储和查询使用起来非常方便,也经常用到统计分析数据中。
awk 第五个实例: 结合Shell使用
这个grepinawk
脚本执行起来非常类似grep -H
命令:
$ cat grepinawk pattern=$1 shift awk '/'$pattern'/ { print FILENAME ":" $0 }' $* $ ./grepinawk strcpy vuln.c vuln.c: strcpy(buffer, argv[1]); $ grep -H strcpy vuln.c vuln.c: strcpy(buffer, argv[1]);
- 这个
awk
脚本结合了shell
脚本的参数实现了类似grep
命令的功能 -
grepinawk
脚本第一个参数传递给pattern
变量 , 然后通过shift
从参数列表中删去。 -
$*
记录的是去掉了地一个参数的参数列表了,这就达到了grep
命令的简单实现。
awk 第六个实例:awk内建函数的使用
## 统计一下本机都与哪些外部地址有TCP连接方法 netstat -tn | awk '/ESTABLISHED/{ LocalAddress = $4; ForeignAddress = $5; ## 按照冒号分割 ForeignAddress 字段内容 n = split(ForeignAddress, arr , ":") if( n == 2) { address[ arr[1] ] ++ } }END{ for( i in address) { printf("%-18s %-5.0f\n", i, address[ i ]); } }' ## 方法二 netstat -tn | awk '/ESTABLISHED/{ gsub(":", " "); i=$6; address[ i ]++ } END{ for( i in address) { printf("%-18s %-5.0f\n", i, address[ i ]); } }'
- 这个实例使用了
split
内建函数,可以将数据重新进行拆分成一个数组,然后返回值为数组数量,需要记住,数组下表是从1
开始的,并不是像C语言
从0开始的。 - 方法二通过使用
gsub
函数也实现了同样效果。
awk 第七个实例: 重定向输出和管道的使用
一个awk命令可能有很多输出内容,并且需要保存到不同名称的文件中,这时候就需要使用重定向输出啦:
netstat -tn | awk '/^tcp/{ state = $NF print $0 > "/tmp/test/"$NF printf("%-20s %-20s %-s\n", $4,$5,$6) | "sort" }'
awk 第八个实例: 函数的使用
>>>>>>>>>>>>$ cat b.txt 100 200 300 1000 40302 2130130 12309 10201 10210 1201 102109 12031 102101 2101 10201 1010 111 10 11121 990 1024 4 101 29 4010 11 230 5 102 7 1021 1012 101 >>>>>>>>>>>>$ awk 'function add (n1, n2) { return n1 + n2 } $1> 100{ printf("%-8.0f + %-8.0f = %10.0f\n", $1 , $2 , add( $1, $2)) }' b.txt 1000 + 40302 = 41302 12309 + 10201 = 22510 1201 + 102109 = 103310 12031 + 102101 = 114132 10201 + 1010 = 11211 990 + 1024 = 2014 >>>>>>>>>>>>$
原文地址:https://www.jianshu.com/p/3b393628a544
相关推荐
-
Linux ln 命令 服务器
2019-9-8
-
MySQL事务,事务隔离级别详解 服务器
2019-7-22
-
ddgr:一个从终端搜索 DuckDuckGo 的命令行工具 服务器
2019-7-2
-
Hyperledger Fabric、Corda和以太坊对比 服务器
2019-9-10
-
浅析系统高可用 服务器
2019-8-26
-
centos安装rabbitmq 服务器
2019-5-12
-
添加 Ubuntu/Debian 到 RHEL/CentOS 7的 PXE 网络启动环境 服务器
2019-3-14
-
MySQL数据库之索引 服务器
2019-3-25
-
Bash 中的逻辑和(&) 服务器
2019-5-6
-
SQL优化指南 服务器
2019-5-10