文章目录
既是linux中的一条命令,也是一种编程语言,用于处理数据和生成报表。awk由三个人名组成,本身没什么特别的意义。
语法
awk [options] '[pattern]{actions}' [var1=value1 var2=value2 ...] file ...
awk [options] -f scripts_filename [var1=value1 var2=value2 ...] file ...
options 选项
-F
:field separator,字符分割符,默认为空白符(包括空格、制表符等)-f SCRIPTS_FILE
:用于执行指定的脚本文件-v
:变量声明awk -v a=1 -v b=2 '{print a,b}'
pattern 模式
BEGIN
- 指定在第一条记录被处理前发生的动作
- 用于初始化语句、内置或用户变量的定义,初始化格式
END
- 指定在最后一条记录被处理后发生的动作
- 用于总结输出
- 处理中(空)
- 对所有行应用指令
/RegExp/
:处理正则表达式匹配到的行/RegExp1/, /RegExp2/
:指定一个行的范围,如/^a/, /^b/
- 条件表达式
- 关系运算符:>,<,>=,<=,==,!=,用于字符串或数字的标胶
- 正则匹配表达式:~,!~,如
awk -F: ‘$1 ~ /^a.*e$/{print $1,$3}’
action 操作
- 变量或数组赋值操作
var=value
name="alice"
- 数组
a["age"]="haha"
a[1]="xxx"
- 格式化输出
- 指定字段用
$N
表示,$0
为整行内容,引用变量不需要加$
pirnt
printf
:跟c语言用法差不多,可以用%s
,%d
等占位
- 指定字段用
- 内置函数
sub
substr
- 等
- 控制流命令
if/else
for
while
awk脚本文件格式
-
一行中多条语句用分号分隔:
action1;action2
-
每条语句在不同行,可以不想需要分号分隔
-
如果操作跟在某个模式后面,它的左大括号就必须与该模式在同一行(像java的习惯写法)
BEGIN { action1 action2 }
-
注释:
#
记录和字段
- 记录定义:Record,文件的每一行被称为一条记录,以换行符分隔
- 输入数据具有固定格式的结构
- 不是无休止的字符串
- 内置变量:
$0
:当前行内容,当前记录的内容ORS
:output record separator,记录输出分割符,默认为换行RS
:record separator,记录分割符,默认为换行NR
:number of records,当前处理的总记录编号FNR
:file’s number of record,当前处理的文件的记录编号,awk可以处理多个文件,当读取一个新文件时FNR
会重新开始计数,所以FNR
≤NR
- 字段定义:Field,每条记录由多个字段组成,字段之间用分割符分开
- 内置变量:
OFS
:output field separator,字段输出分割符,默认为空格FS
:field separator,字段分割符,默认为空白符NF
:number of fields,当前记录字段数
- 字段分割:
- 通过 -F 选项来修改
FS
的指:awk -F: ‘{print $1}’
- 同时使用多个字段分割符,将分割符放入[]中:
echo -e “a:b\tc” | awk -F’[:\t]’ ‘{print $1, $3}’
- 通过 -F 选项来修改
- 内置变量:
格式化输出
- 参数可以是变量、计算表达式、字符串变量
- 字符串必须用双引号引起来:
echo 300 2 3 4 | awk '{print "hello" }'
- 参数之间用逗号分隔:
echo 300 2 3 4 | awk '{print $1,$2 }'
- 输出可以被重定向:
echo 300 2 3 4 | awk '$1 * $2 > 500{print $3+$4 >> "/tmp/test" }'
- 输入和输出可以通过管道:
echo 300 2 3 4 | awk '$1 * $2 > 500{print $3+$4 | "grep 7" }'
- 注意这里是将所有该操作的结果通过管道
- 转义序列
\n
:换行符\t
:制表符\r
:回车\047
:八进制值47
printf
-
返回给标准输出一个带有格式的字符串:f,format
-
不会在行尾自动换行
-
包含一个加“”的控制字符串
-
修饰符
-
:左对齐,默认右对齐+
:数字带正负符号(+,-)
-
格式说明转换符
%
%c
:character,字符%s
:string,字符串%d
:digit,整数%f
:fload,浮点数
[root@localhost ~]# echo alice 23 01 |awk ' {printf "The name is: %-15s ID is %8d\n",$1,$3}'
The name is: alice ID is 1
练习
- test文件内容:
Mary 2143 78 84 77
Jack 2321 66 78 45
Tom 2122 48 77 71
Mike 2537 87 97 95
Bob 2415 40 57 62
- 输出成绩表:
Lineno. Name No. Math English Computer Total
------------------------------------------------------------
1 Mary 2143 78 84 77 239
2 Jack 2321 66 78 45 189
3 Tom 2122 48 77 71 196
4 Mike 2537 87 97 95 279
5 Bob 2415 40 57 62 159
------------------------------------------------------------
Total: 319 393 350
Avg: 63.8 78.6 70
- 通过
.awk
BEGIN {
printf "%-10s%-10s%-10s%-10s%-10s%-10s%-10s\n","LNO","Name","No","Math","English","Computer","Total"
printf "------------------------------------------------------------------\n"
}
{
math+=$3;english+=$4;com+=$5;printf "%-10s%-10s%-10s%-10s%-10s%-10s%-10s\n",NR,$1,$2,$3,$4,$5,$3+$4+$5
}
END {
printf "------------------------------------------------------------------\n"
printf "%-30s%-10s%-10s%-10s\n","Total:",math,english,com
printf "%-30s%-10s%-10s%-10s\n","Avg:",math/NR,english/NR,com/NR
}
- 通过命令
awk 'BEGIN{math=0;eng=0;com=0;printf "Lineno. Name No. Math English Computer Total\n";printf "------------------------------------------------------------\n"}{math+=$3; eng+=$4; com+=$5;printf "%-8s %-7s %-7s %-7s %-9s %-10s %-7s \n",NR,$1,$2,$3,$4,$5,$3+$4+$5} END{printf "------------------------------------------------------------\n";printf "%-24s %-7s %-9s %-20s \n","Total:",math,eng,com;printf "%-24s %-7s %-9s %-20s \n","Avg:",math/NR,eng/NR,com/NR}' test
编程结构
关系表达式
< , <= , >, >= , == , !=
~ 和 !~
:匹配/不匹配操作符,用于对记录或字段的表达式匹配awk '$1 ~ [Ll]ove{printf }' file
awk '$1 !~ [Ll]ove' file
复合模式
&&
:逻辑与echo | awk 'a>b && a!=0 {print a}' a=2 b=1
||
:逻辑或echo | awk 'a>b || a!=0 {print a}' a=1 b=2
!
:逻辑非echo | awk '! a!=0 {print a}' a=0 b=2
条件赋值
var=(EXP)?var1:var2
:其实就是三目表达式- echo| awk’{max=(a>b)? a:b; print max}’ a=1 b=2
范围模式
/RegExp1/,/RegExp2/
awk '/^a/,/^b/{print $0}' file
算数运算
+,-,*,/,%,^或**
:加,减,乘,除,求余,求幂++,--
:一元加减=,+=,-=,*=,/=,%=,^=,**=
:赋值运算符
函数
内置函数
字符串函数
-
sub(/RegExp/,"STR1"[,"STR2"]) FILENAME
:substitute,替换- 在记录中查找能够匹配正则表达式的最长最靠左子串,然后用替换串
STR1
替换找到的子串 - 如果指定了目标串就对目标串查找替换,不指定则对当前记录处理
- 只对每行出现的第一个匹配进行替换
- 例:搜索STR2中RegExp匹配的内容,替换成STR1
echo "abca" | awk '{sub(/a/,"b");print}'
echo "ab:ca" | awk -F: '{sub(/a/,"b",$1);print}'
- 在记录中查找能够匹配正则表达式的最长最靠左子串,然后用替换串
-
gsub(/RegExp/,"STR1"[,"STR2"]) FILENAME
:global substitute,全局替换,用法和sub
一样 -
index("字符串","查找子串")
:返回子串在字符串中第一次出现的位置,索引号从1开始计算,如无返回0echo "ab:cd" | awk -F: '{print index($1,"a")}'
echo "ab:cd" | awk -F: '{print index($1,"b")}'
-
substr("字符串",开始位置,长度)
:返回从字符串指定位置开始的子串echo "abcdef" | awk '{print substr($0,1,3)}'
-
match("字符串",/正则表达式/)
:返回正则表达式在字符串中出现的位置,如果未出现则返回0-
内置变量
RSTART
为匹配到的子串在字符串中的起始位置,下标从 1 开始 -
内置变量
RLENGTH
为子串的长度 -
可以通过
substr
提取出匹配到的子串 -
echo "abcdefABC" | awk '{match($0,/[[:upper:]]+/);print RSTART,RLENGTH;print substr($0,RSTART,RLENGTH)}'
[root@localhost ~]# echo "abcdefABC" | awk '{match($0,/[[:upper:]]+/);print RSTART,RLENGTH;print substr($0,RSTART,RLENGTH)}' 7 3 ABC
-
-
split("字符串",数组名,"字段分隔符")
:使用第三个参数指定的字段分割符将字符串拆分成一个数组echo "18/01/31" | awk '{split($0,date,"/");print date[1],date[2],date[3]}'
-
length("字符串")
:返回字符串的长度(以字符为单位) -
blength("字符串")
:返回字符串的长度(以字节为单位) -
tolower("字符串")
:字符串中每个大写字符将更改为小写 -
toupper("字符串")
:字符串中每个小写字符将更改为大写
时间函数
-
mktime( YYYY MM DD HH MM SS)
:生成用一串数字表示的时间戳awk 'BEGIN{tstamp=mktime("2013 01 04 12 12 12"); print tstamp}'
[root@localhost ~]# awk 'BEGIN{tstamp=mktime("2013 01 04 12 12 12"); print tstamp;}' 1357319532
-
systime()
:得到时间戳,返回从1970年1月1日开始到当前时间(不计闰年)的整秒数 -
strftime([格式 [, 时间戳]])
:格式化时间输出,将时间戳转为时间字符串,省略时间戳表示当前时间[root@localhost ~]# awk 'BEGIN{tstamp=mktime("2013 01 04 12 12 12"); print strftime("%c", tstamp);}' 2013年01月04日 星期五 12时12分12秒 [root@localhost ~]# awk 'BEGIN{tstamp=mktime("2013 01 04 12 12 12"); print strftime("%c");}' 2020年03月24日 星期二 05时29分42秒 [root@localhost ~]# awk 'BEGIN{tstamp=mktime("2013 01 04 12 12 12"); print strftime();}' 二 3月 24 05:32:04 EDT 2020
算数函数
int(x)
:取整rand()
:返回随机数 n ,其中 0 ≤ n <1sqrt(x)
:开平方
其他函数
sytem("CMD")
:调用系统命令执行
自定义函数
# 格式
function_name(参数1,参数2,...){
statements
return expression
}
变量
用户自定义变量
- 变量名由字母、数字、下划线组成,不能以数字开头
- awk可以从上下文推导出它的数据类型
- 字符串变量要括在“”中
内置变量
属性 | 说明 |
---|---|
$0 | 当前记录(作为单个变量) |
$1~$n | 当前记录的第n个字段,字段间由FS分隔 |
FS | 输入字段分隔符 默认是空格 |
OFS | 输出字段分隔符 默认也是空格 |
RS | 输入的记录分隔符,默认换行符 |
ORS | 输出的记录分隔符,默认为换行符 |
NF | 当前记录中的字段个数,就是有多少列 |
NR | 已经读出的记录数,就是行号,从1开始 |
FNR | 该文件当前记录数 |
ARGC | 命令行参数个数 |
ARGV | 命令行参数数组 |
FILENAME | 当前输入文件的名字 |
IGNORECASE | 如果为真,则进行忽略大小写的匹配 |
RSTART | 被匹配函数匹配的字符串首 |
RLENGTH | 被匹配函数匹配的字符串长度 |
重定向
输出重定向
>
:echo | awk '{print "abc" > "test" }
>>
输入重定向
getline
- 如果得到一条记录,getline函数返回1,如果到达文件的末尾就返回0,如果出现错误,例如打开文件失败,就返回-1,可以结合到while等流控制语句使用
awk 'BEGIN{getline < "-";print}'
awk 'BEGIN{getline < "test.txt"; print $0}'
awk 'BEGIN{ while( getline line < "test" ){ print line } }'
管道
可以在awk打开一个管道,但同一时刻只能由一个管道存在:若第一个管道没有被关闭就使用第二个管道,则数据仍会传到第一个管道中,因为前一次管道里的数据和文件描述符(指针)还建立着连接。通过close()可关闭管道
awk '{print $1, $2 | "sort" }' test END {close("sort")}
条件语句
-
if/else
if (expression) { statement; ... } else if (expression) { statement; ... } else { statement; }
循环
-
while
while (expression) { statement; ... }
-
for
for (i=1;i<=NF;i++){ statement; ... } for (i in Array){ # 注意这里取出来的i为Array的key print i,Array[i] }
程序控制
next
:跳过当前记录,读取下一条记录exit N
:退出awk执行
数组
- 下标可以是数字或字符串(相当于字典)
- 数组元素不是顺序存储
- 下标通常称为键(key)
- 变量可以作为数组下标
- 字段值可以作为数组下标
- 循环遍历数组中的元素:
for (i in Arrayname) {print Arrayname[i]}
- 这里读出来的 i 是 key 而不是 value
- 统计tcp的链接数
[root@localhost ~]# netstat -ant | awk '/^tcp/ {++state[$NF]} END {for(key in state) print key,"\t",state[key]}'
LISTEN 12
ESTABLISHED 2
- 分别统计不同ip的tcp链接
[root@localhost ~]# netstat -ant | awk '/^tcp/ {n=split($(NF-1),array,":");if(n<=2)++S[array[(1)]];else++S[array[(4)]];++s[$NF];++N} END {for(a in S){printf("%-20s %s\n", a, S[a]);++I}printf("%-20s %s\n","TOTAL_IP",I);for(a in s) printf("%-20s %s\n",a, s[a]);printf("%-20s %s\n","TOTAL_LINK",N);}'
* 6
192.168.159.1 2
0.0.0.0 6
TOTAL_IP 3
LISTEN 12
ESTABLISHED 2
TOTAL_LINK 14
[root@localhost ~]# netstat -ant | awk '/^tcp/ {n=split($(NF-1),array,":");if(n<=2)++S[array[(1)]];else++S[array[(4)]];++s[$NF];++N} END {for(a in S){printf("%-20s %s\n", a, S[a]);++I}printf("%-20s %s\n","TOTAL_IP",I);for(a in s) printf("%-20s %s\n",a, s[a]);printf("%-20s %s\n","TOTAL_LINK",N);}'
* 6
192.168.159.1 2
0.0.0.0 6
TOTAL_IP 3
LISTEN 12
ESTABLISHED 2
TOTAL_LINK 14