shell--细说awk

1 awk 简述

awk 是 Linux/UNIX 下的用来操纵数据和产生报告的程序语言。 Nawk 是新的版本, gawk
是 Gnu 版本。awk 可用于命令行的简单操做,也能够写入大的应用程序。由于 awk 能够操纵数据,因此它是 Shell脚本和管理小型数据库中必需的工具。linux

1.1 编程模型

    awk 执行过程分为三个阶段:读输入文件以前执行的代码段(由BEGIN关键字标识);主循环执行输入文件的代码段;读输入文件以后的代码段(由END关键字标识)。可参考下图c++

awk 编程模型

1.2 基本概念

    awk 结构由模版和动做组成。模版用于筛选数据行,一般由正则或条件表达式构成;动做是由一对大括号包裹的代码组成。他们都是可选的。正则表达式

#模式能够是正则、条件表达式或两者的结合。模式不须要写在大括号内
#模式规定了触发大括号内的动做的条件

awk '/root/' txt.log #只有模版,输出含有root的行
awk -F : '$3 == $4' txt.log #只有模版,输出uid=gid的行
awk -F : '{if($3 == $4) echo print $0}' txt.log #区别于模式,动做要写在括号内
awk '/root/{print $0}' txt.log #{}中是动做
awk '{print $0}' txt.log #只有动做,表示处理全部输入行

#范围匹配 '/root/, /mail/' 等同于sed
awk -F: '/root/, /daemon/' txt.log

#多个语句
awk -F: '/root/{print $0}{print $1}' txt.log #一个模版只能结合一个动做大括号,后一个是新的开始

两种调用模式:awk -F 域分隔符  'awk程序段'  输入文件   或   awk   -f xx.awk   输入文件shell

记录和域:awk 将文件行定义为记录,行中的字符串定义为域。域之间用空格、Tab键或其它字符分割。$为域操做符,$0表示整行记录,$1...$n 表示第几个域,也支持表达式 $(a+b) a,b 为整数。-F指定域分隔符,能够是单个字符或正则表达式,默认是空格,Tab被认为是多个空格。字符串跟变量用双引号拼接。数据库

awk -F ':' '{print NF}' txt.log # 一个冒号
awk -F ':+' '{print NF}' txt.log # 为正则时以贪婪匹配为准且第一个字符不会被解析为正则符号

2 变量

    不须要声明能够直接使用,类型是由其内容决定的,变量名区分大小写。编程

     数据类型:数字(整型和浮点型 )、字符串、null、数组数组

    赋值语句跟C语言相同,再也不赘述,默认初始化为0或空字符。bash

#变量的使用
awk -F ':' 'BEGIN{one=1;two=2;print res} {res+=$(one+two);print res' txt.log

2.1 内置变量

awk 内置了许多有用的变量,可使用man命令细查,下边列出经常使用的函数

变量名 释义
ARGC 命令行中参数的个数
AGRIND 命令行中当前文件的位置,下标从0开始
ARGV 命令行参数的数组
ENVIRON 环境变量管理数组
FILENAME 当前文件名
FNR 浏览每一个文件的记录数
FS/RS 域分隔符,默认空格 | 记录分隔符,默认空格
IGNORECASE 布尔值,为真则忽略大小写匹配
NF/NR 当前记录中域数量,每行有可能不一样 | 当前记录数,不一样于FNR,它记录的是总行数
OFMT/OFS/ORS 数字输出格式 | 输出域分隔符、默认空格 | 输出记录分隔符,默认换行符
SUBSEP 数组下标分隔符,默认\034,能够用来模拟实现二维数组

2.2 数组使用

awk 支持关联数组,下标也能够是变量。使用for (key in array) array[key] 来遍历工具

#数组赋值、删除【delete】、遍历【for in】、长度【length()】
awk -F: '{name[NR]=$1;} END{print length(name);delete name[1];for(n in name) print n, name[n]}' txt.log
#注意:name["a"]=3  if("a" in name) print "exist"   in 能够断定键是否存在

2.3 参数变量

    能够经过命令行参数将变量传递到awk 的BEGIN 块里,有两种方式

#-v 参数传递变量
awk -v a=1 -v b="hi dude" 'BEGIN{print a, b}'

#awk 脚本
##======test.awk========
 # {print a, b}
##======================
awk -f test.awk a=1 b="hi dude" txt.log #在begin模块访问不了参数变量

3 运算符

    包括整数运算,比较运算符、逻辑运算符 跟C语言相同。只是多了 x~/y/  x!~ /y/ 两个正则匹配符

#关系运算符 > >= < <= == !=   ~  !~
awk -F: '$0 ~ /root/' txt.log # 正则匹配符
awk -F: '$0 !~ /root/' txt.log # !表示非
awk -F: '$3>=20 {print $1}' txt.log # == !=  

#条件表达式
awk -F: '{max=$4>$3 ? $4 : $3;print $0,max}' txt.log 

#布尔运算符 &&  ||  !
awk -F":+" 'NF == 6 || $1 ~ /root/ {print $0 " show"}' txt.log

#算术运算  + - * /(非整除) %  ^或** ++ --
awk -F: '{print $3 "+10=" $3+10, x+=1}' txt.log 
awk -F: '{print $3 "/2=" $3/3}' txt.log # 保留6位有效小数
awk -F: '/^$/ {++x} END{print "有"x"个空行"}' txt.log # 未初始化的整型默认为0

4 流程控制

    if 语句同C语言一致,属于动做语句。

#if 用法示例,多个语句须要大括号,之间分号间隔
awk -F: '{
	if($1 ~/root/) msg="root";\
	else if($1 ~/mail/) msg="mail";\
	else msg="unkonwn";
}END{print msg}' txt.log

循环示例

#while 相似还有do while
awk -F: '{i=1;while($i<=NF){print NF, $i, $i++;}}' txt.log

#for 有两种模式
for (n in array) 专用于数组
awk -F: '{for(i=1;i<=NF;i++)print NF,$i;}' txt.log

#循环控制
continue break 与C语言同
next 会阻止其在同一个命令块中后边的命令,而后从头执行
exit 会退出整个awk 命令块,直接跳转到END模块(若是有)

5 函数

也能够自定义函数,使用较少,主要介绍系统函数。

#字符串函数示例

#sub(/正则/,"替换字符","目标字符串,可省略默认是$0"),使用的是贪婪匹配,且只替换第一次匹配
#gsub 与sub 语法格式相同,区别在因而所有替换
awk -F: '{sub(/r.+t/, "===");print}' txt.log #是贪婪匹配

#index(string, substring) 返回子串第一次出现的位置,下标从1开始
awk -F: 'BEGIN {print index("hello", "l")}'

#length(string) 返回字符数,省略参数表示则默认为$0
#substr(string,start=1,length),省略或超出总长都默认到末尾,不能为负数
#match(string, /pattern/)返回第一次匹配的字串位置并保存在RSTART中,字串长度保存在RLENGTH,不匹配返回0
awk -F: 'BEGIN {start= match("Good old USA", /[A-Z]+$/);print RSTART,RLENGTH,start}'

#toupper(str), tolower(str) 大小写转换,仅gawk支持;sprintf(str) 格式化输出
#split(string,array,sep=FS),将返回的数组放到array中,默认分隔符为FS,无匹配则返回整个字符到第一个元素中
awk -F: 'BEGIN{split("2017-11-29",date);print date[1];}'

#时间函数 systime  strftime

#systime() 返回unix时间戳(秒) awk 'BEGIN{print systime()}'
#strftime("格式符" [,时间戳=当前]) 格式符与C库strftime 一致,可 man strftime 查看
awk 'BEGIN{print strftime("%D")}'

#数学函数 int  rand  srand

#int() 去掉小数部分,没有舍入
#rand() 生成0-1的随机小数,每次执行产生的值都同样
awk '/root/{print rand()}' txt.log #执行两遍,输出的结果同样
#srand(x) 生成rand的种子,x默认是时间戳
awk 'BEGIN{srand()} /root/{print 1 + int(25*rand())}' txt.log

6 重定向和管道

#输出重定向(>和>>), 文件路径需要引号包裹,文件一旦被打开,必须明确关闭或等待awk程序结束
awk -F: '$3>20 {print $0>"/tmp/test.log"}' txt.log

#输入从新定向 getline 函数。它能够从标准输入、管道,外部文件获取下一行输入。
#会修改NF,NR,FNR,若是获得一个记录返回1,达到文件末尾返回0,出现错误返回-1
#从管道获取
awk 'BEGIN{"date"|getline now;print now}' 
awk 'BEGIN{while("ls"|getline)print}'

#从命令行获取
awk 'BEGIN{printf "What is your name?";\
getline name <"/dev/tty"}\
$1~name{print "found"name"on line",NR"."}\
END{print "See ya,"name"."}' txt.log

#从外部文件获取
awk 'BEGIN{while(getline < "/etc/passwd">0)lc++;print lc}'

管道:若是在awk中已打开一个管道,那么在打开下一个管道前必须关闭它,管道符号右边能够经过双引号关闭管道。

system("linux shell")  能够执行系统命令,正常的执行系统命令须要用双引号包裹

相关文章
相关标签/搜索