2017-05-11
来源:
- http://man.linuxde.net/awk
- 沈剑老师的公众号
awk是一种编程语言, 用于在linux、unix下对文本和数据进行处理, 数据可以来自标准输入(stdin), 一个或多个文件, 或其他命令输出, 它支持用户自定义函数和动态正则表达式等先进功能, 是linux/unix下的一个强大编程工具, 它在命令行中使用, 但更多是作为脚本来使用。awk有很多内建功能,比如数组、函数等, 这是它和C语言的相同之处, 灵活性是awk最大的优势。
awk命令格式和选项
原理:
逐行处理文件中数据
awk 'BEGIN{commands} pattern{commands} END{commands}'
**BEGIN语句块在awk开始从输入流读取行之前被执行, 这是一个可选的语句块, 比如变量初始化、打印表格表头等语句
**
**END语句块在awk从输入流读完所有的行之后被执行,比如打印所有行的分析结果这类信息汇总走势在END语句块中完成,他也是一个可选语句块
**
**pattern语句块中的通用命令是最重要的部分,直接作用于文件的每一行数据,它也是可选的, 如果没有提供pattern语句块,则默认执行{print}, 打印每一行
**
语法:
awk 'pattern + {action}'
# awk '模式 + {操作}' 模式可以省略, 将模式写入{command}中
awk 'NR==20{print $1, $2}'
awk '{if(NR==20)print $1, $2}'
说明:
- 单引号 是 为了和shell命令区分开
- **大括号{}标识一个命令分组 **
- pattern是一个过滤器, 表示命中pattern的行才能进行action操作 BEGIN等语句在pattern中
- atcion是处理动作
- 使用#作为注释
常用命令选项:
-F fs fs指定分割符,默认空格,fs可以是字符串或正则表达式,如-F:
-v var=value 复制一个用户定义变量, 将外部变量传递给awk
-f scripfile 从脚本文件中读取awk命令
-
-m[fr] val 对val值设置内在限制(标准awk不适用)
-mf 设置给val的最大块数目
-mr 限制记录的最大数目
模式:
- /正则表达式/ : 使用通配符的扩展集
- 关系表达式: 字符串或数字比较操作 == 、!= 、 > 、 => 等
- 模式表达式: ~(匹配于) 、 ~!
- BEGIN / pattern / END语句块:
操作:
**操作是由一个或多个命令、函数、表达式组成,之间由换行符或者分号隔开,并位于{}内**
-
变量或者数组赋值
赋值 count=0
-
数组:
#awk数组不必提前声明,也不必声明大小, 数组元素用0或者空字符串来初始化, 这根据上下文而定, awk数组可以用数字或者字符串作为下标(其实类似python、java中字典) Array[1]="sun" Array[2]="kai" Array["first"]="www" Array["last"]="name" #读取数组的值 {for(item in array) {print array[item]}; } #输出顺序是随机的 {for(i=1;i<=len;i++) {print array[i]};} # len是数组的长度 # 数组的长度 awk 'BEGIN{info="it is a test";lens=split(info, tA, " ");print length(tA), lens;}' # 4 4 awk 'BEGIN{info="it is a test";lens=split(info, tA, " ");print asort(tA);}' # asort对数组排序函数, 本身也返回数组长度(asort函数没定义???) # 数组读取 awk 'BEGIN{info="it is a test";lens=split(info, tA, " ");for(k in tA){print k, tA[k];}}' # 随机输出 for k in tA k是键值, 这点和python字典中一样 # 2 is # 3 a # 4 test # 1 it awk 'BEGIN{info="it is a test";lens=split(info, tA, " ");for(k=1;k<=lens;k++)print k, tA[k];}' # 按顺序输出, 很像C语言,有木有,一样的;结束,一样的大括号战役(只有一句可以省略{},但是操作外面的{}必须有), 但是注意数组下标从1开始 # 啊? 如果想删除数组的键值怎么办(数组这个词怪怪的,类似字典) delete Array['a'] #判断是否包含 if("a" in Array) print "ok" ## 二维数组: 除了不用预先声明外, 赋值神马的和C语言一样
输出命令
-
内置函数
-
gsub(Ere, Repl, String): 在String, 用Repl替换掉Ere, 原位置替换,String被改变
awk '{gsub(r, s)}' # 在$0中用s替代r, 默认Sring=$0 awk 'BEGIN{info="this is a test2010test!";gsub(/[0-9]+/, "!", info);print info}'
index(s, t): 返回s中t的第一个位置, 没有找到回0
length(s): s的长度(字符形式, 中文算一个)
blength(s): s的长度(字节形式, 和编码有关)
match(String, Ere): String是否匹配Ere 未找到, 返回0
split(s, a, fs): 在fs上将s分成序列a, a是一个数组,fs是分隔符,缺省为空格 函数本身返回数组的长度
substr(s, p): 返回s从p开始的子串
tolower(String) 返回小写String
toupper(String)
printf(Format, Expr, Expr) 格式化字符串输出
-
print 普通正常输出, 加不加括号都行
%d %u %f %s %c %p %e %x %o %g 复习C,一模一样 %g 自动选择合适的表示法 awk 'BEGIN{n0="金价为";n1=124.113;n2="较昨日上涨";n3=-1.224;printf("%s%.2f,%s%g\n", n0, n1, n2, n3);}'
-
-
控制语句
-
条件判断
if(....) {....} else {....}
-
循环
while(....) {....} for(k in Array) # 注意k是键值,并不是数组的内容 {....} for(k=0;k<=20;k++) {....} do(....) while(条件) break continue next ?? exit 使程序中断直接到END语句块, 如果在END中出现, 则终止脚本
-
内置变量:
- FS 分隔符,默认空格
- NR 当前行数,从1开始
- NF 当前记录字段个数 依次类推 $NF $(NF-1)
- $0 当前记录 全行
- $1~$n 当前记录的第n个字段 当前行第几个字段从1开始(注意是字段,以默认FS分割开的)
exp:
cat test | awk '/.*?山$/'
awk '/^亏/' test
# 显示匹配hello的行, action默认即为print
# 这个我们可以改写成在模式里面用match函数匹配
awk '{print match($0, /[0-9]+/)?"ok":"no found";}' test
# ?: 符号也是C里面的, /[0-9]+/ 是正则匹配, 输出匹配结果呢?如下, 省略BEGIN语句块
awk '{if(match($0, /山/))print $0}' test
cat hello.txt | awk 'length($0) > 80{parint NR}'
# 打印长度大于80的行数, 注意打印的是行数 print NR行数,如果想打印本身$0
cat hello.txt | awk 'NR==3, NR==5{print $1, $NF}'
# 打印3 ,5 行第一列和最后一列
cat test | awk -F, 'NR==36446{print $1, $NF}'
# 改为中文逗号分割 等同于:
awk -F, 'NR==36446{print $1}' test
# 或者在BEGIN配置需要的变量,然后pattern选择条件 {command}处理
awk 'BEGIN{FS=","}NR==36446{print $1}' test
awk 'BEGIN{FS=","}{if(NR==36446)print $1}' test
其它
-
输出到一个文件
awk 'BEGIN{FS=","}length($0)>=20{print $1}' test > test2 awk 'BEGIN{FS=","}length($0)>=20{print $1 > "test2"}' test awk 'BEGIN{FS=","}length($0)>=20{print $1 >> "test2"}' test # 这三句效果一样
-
读取外部输入
-
getline
awk 'BEGIN{"date" | getline out; print out;}' test awk 'BEGIN{"date" | getline out; split(out, mon);for(k in mon)print k, mon[k]}' awk 'BEGIN{"date" | getline out; lens=split(out, mon);for(k=1;k<=lens;k++)print k, mon[k]}' awk 'BEGIN{while("ls" | getline) print}' #用while语句 对ls的输出逐行处理(在正式处理输入前), 如果不用while的话默认是第一行 awk 'BEGIN{"ls" | getline; print}' #用户手动输入 awk 'BEGIN(print "Enter your name:";getline name; print name)'")}'
-
-
打开外部文件: 依然是利用管道 + getline来交互
awk 'BEGIN{while("cat test"|getline out)print $0;close("test")}' # 这样子是打不出来的, 管道传递的值都给out了,$0每行都是空 awk 'BEGIN{while("cat test"|getline out)print out;close("test")}' awk 'BEGIN{while("cat test"|getline)print $0;close("test")}'
-
调用外部应用程序 调用shell命令
-
system("....") 调用linux命令, 返回liunx输出结果
awk 'BEGIN{b=system("ls -al");print b;}' # 最后为啥会有个0 ?? 求解释?? # 或者我们可以利用管道更简单些 awk 'BEGIN{"date"|getline b; print b}'
-
-
时间函数
mktime(YYYY MM dd HH MM ss[DST]) 生成时间格式
strftime([format [, timestamp]])
-
systime() 得到时间戳
awk 'BEGIN{tstamp=mktime("2001, 01 01 12 12 12");print strftime("%c", tstamp);}' # awk version 20070501中没有mktime的函数, 2001年....
写了很多,但是常用的应该是BEGIN{}pattern{}END{}来处理常规的文本数据,日期什么的直接shell就好了
更多详细数据格式 http://man.linuxde.net/awk