awk对输入的行进行分割操作,然后对分割后的列集合进行操作。列集合中的元素下标从1开始,$1代表第一列,以此类推。在默认情况下分割符采用的是空格,当然你可以根据不同的业务需求修改分隔符。
参数
awk [ -F fs ] [ -v var=value ] [ ‘prog’ | -f progfile ] [ file … ]
-F fs
用于指定分隔符,默认情况下为空格
echo "abc:def:ghi" | awk '{print $1}'
输出
abc:def:ghi
由于上面的文本中没有空格,因此采用空格分割的时候,该文本被当做了一列然后输出。如果将上面的命令修改为
echo "abc:def:ghi" | awk -F ":" '{print $1}'
输出将变为 abc
-v var=value
-v参数用于指定变量,指定的变量可以在后续的处理中用到。-v参数可以出现多次。
echo "abc:def:ghi" | awk -v test=test1 -F ":" '{print $1" "test}'
输出
abc test1
在-v参数中指定了变量test,其值为test1,所以在后面打印test的时候,输出了test1。
echo "abc:def:ghi" | awk -v test=test1 -v test1=test2 -F ":" '{print $1" "test" "test1}'
输出
abc test1 test2
上面的例子中使用了-v定义了两个变量
‘prog’
分割后,需要执行prog对其作出处理
echo "abc:def:ghi" | awk -F ":" '{print $1}'
上面的例子中prog = {print $1},其含义是打印出分割后该行的第一列元素。
-f progfile
awk处理的语句除了可以直接写在命令中,还可以在文件中。-f就是指明处理的脚本,分割后的数据会传入到该脚本中进行处理。
新建文件test.awk,写入如下的内容,
#!/usr/bin/awk
# 定义求和函数
function sum(a, b, c) {
return a + b + c
}
# 常规处理模块
BEGIN {
total = 10
}
{
total += sum($1, $2, $3)
}
END {
print total
}
运行下面的命令,
echo "1 2 3" | awk -f test.awk
输出
16
上面的命令从把1 2 3分割成[1, 2, 3],然后读取文件test.awk中的命令对[1, 2, 3]做处理。test.awk做了两件事请
- 定义函数sum
- 初始化值total = 10,然后调用sum对1,2,3求和,然后将sum的结果加上total
awk语法
代码结构
BEGIN {} # 初始化代码块
{} # 处理代码块
END {} # 结尾代码块
- BEGIN用于初始化工作
- END用于结尾工作
- 中间的代码块用于处理输入的每一行数据
条件语句
if( expression ) statement [ else statement ]
例子
echo "1 2 3" | awk '{if ($1 > 1) {print $1} else {print $2} }'
输出 2
循环语句
while( expression ) statement
for( expression ; expression ; expression ) statement
for( var in array ) statement
do statement while( expression )
break
continue
例子
cho "1 2 3" | awk 'BEGIN {i=1} {while(i < 4) { if ($i > 1) print $i; i++}}
输出
2
3
上面的例子中遍历每一列,如果列值大于1则打印出列值,所以打印出了2和3。上面命令等价于
for
echo "1 2 3" | awk '{for (i=1; i<4; i++) { if ($i > 1) print $i}}
do while
echo "1 2 3" | awk 'BEGIN{i=1} {do {if ($i > 1) print $i; i++} while (i < 4)}'
for in
echo "1 2 3" | awk 'BEGIN{a[1]=1; a[2]=2; a[3]=3; }{for (i in a) {if ($i > 1) print $i}}'
操作符
+ # 加法
- # 减法
* # 乘法
/ # 除法
% # 取余
^ # 次方
! # 非
++ # 自增
-- # 自法
+= # 加等
-= # 法等
*= # 乘等
/= # 除等
%= # 取余等
^= # 次方等
> # 大于
>= # 大于等于
< # 小于
<= # 小于等于
== # 等于
!= # 不等于
?: # 双目选择符
[] # 数组取值
|| # 逻辑或
&& # 逻辑与
in # 数组遍历
$ # 取值
~ # 模式匹配(正则匹配)
!~ # 模式不匹配
例子
echo "1 2 3" | awk '{print ($2!=2)?1:2}'
如果$2不等于2则输出1,否则输出2。输出结果为 2
正则表达式
在上面的表达式中讲到了一个运算符”~”(用于模式匹配),awk允许使用该操作符来判断某个属性或者字符串是否匹配某个模式。该运算符的用法是
expression ~ /regulara/ {action}
expression表示需要判断的目标属性或者字符串,regular代表正则表达式, action代表满足条件时需要进行的处理。举个例子: 如果第一列的数据是任意个数小写字母的组合(个数不为0),则打印出第一列的数据
echo "abcdef" | awk '$1 ~ /^[a-z]+$/ {print $1}'
上面的例子会输出 abcdef
,如果修改为下面的命令则不会有任何输出,
echo "abcdef0" | awk '$1 ~ /^[a-z]+$/ {print $1}'
awk使用的正则表达式是extended regular expressions,这里大致介绍一下语法
^ # 字符串的开头
$ # 字符串的结尾
. # 匹配任何符串
[c1c2c3...] # 包含在c1c2c3...中的任意字符
[^c1c2c3...] # 不包含在c1c2c3...中的任意字符
| # 或者
() # 提供group操作, 可将括号内的数据当做一部分
* # 任意个数
+ # 大于0的任意个数
? # 0或者1个
echo "abcdef0" | awk '$1 ~ /^[a-z]+$|^[a-zA-Z0-9]*$/ {print $1}'
上面的例子输出为 abcdef0
, 这里用到了”^”, “$”, “[]”, “|”, “*”, “+”
echo "abcdef" | awk '$1 ~ /^(abcdef)?$/ {print $1}'
上面的例子输出为 abcdef
,这里用到了”^”, “$”, “()”, “?”
内置函数
-
gsub(r,s,t)
t中所有满足正则表达式r的子串都用s来进行替换,函数返回被替换的次数。如果t不指定,则默认使用$0,也就是整行数据。
echo "abc123" |awk '{gsub(/[0-9]+/, "replace", $1); print $1}'
上面的例子中对数字子串进行了替换,输出为
abcreplace
-
index(s, t)
返回子串t在目标串s中的开始下标,如果没有找到则返回0,s的第一个字符的下标是1。
echo "abc123" |awk '{i=index($i, "123"); print i}'
输出为
4
-
length(s)
返回字符串s的长度。
echo "abc123" | awk '{print length($1)}'
输出为
6
- match(s, r)
返回s中满足正则表达式r的子串的第一个下标。
echo "abc123" | awk '{print match($1, /[0-9]+/)}'
输出为
4
-
split(s, A, r)
使用正则表达式对s进行分割,分割后的数组存放在A中。如果r不指定,则采用fs(-F参数指定的分隔符)进行分割。
echo "abc123def" | awk '{split($1, A, /[0-9]+/); for (i in A) print A[i]}'
输出为
abc def
-
sprintf
格式化输出
echo "abc123" | awk '{print sprintf("hh%s", $1)}'
输出为
hhabc123
-
sub(r, s, t)
功能与gsub相类似,不过只能处理一个匹配,之后的匹配不做任何处理
-
substr(s, i, n)
返回字符串s从i开始长度为n的子串
echo "abc123" | awk '{print substr($1, 1, 3)}'
输出为
abc
-
tolower(s)
转变为小写字母
-
toupper()
转变为大写字母
- atan2
- cos
- exp
- int
- log
-
rand
随机数
- sin
- sqrt
-
srand
设置随机数种子
内置变量
ARGC 命令行参数的个数
ARGV 命令行参数数组
CONVFMT 内置的用于将数字格式化成string的模板,默认是"%.6g"
ENVIRON 环境变量数组
FILENAME 当前文件名,也就是需要进行数据处理的文件名。如果没有指定文件名,则默认返回"-"。
FNR 当前文件处理的行数
FS 分隔符
NF 当前记录分割后的列数
NR 所有文件处理的行数
OFMT 打印数字的模板,默认是"%.6g".
OFS 列输出的时候插在每列之间的字符串
ORS 每行输出后所添加的字符串
RLENGTH 上一次调用match()函数所匹配出的字符串的长度
RS 输入的所有行的分隔符,默认是"\n"
RSTART 上一次调用match()函数所匹配出的字符串在原始字符串中的开始下标
SUBSEP 数组访问时可以使用多条件,SUBSEP就是多条件的分割,默认是"\034"
- OFS的用法
echo "abc 123 222" | awk 'BEGIN{OFS="$"} {print $1,$2,$3}'
输出为
abc$123$222
- ORS的用法
echo "abc 123 222" | awk 'BEGIN{OFS="$"; ORS="*"} {print $1,$2,$3}'
输出为
abc$123$222*
- FNR, NR
echo -e 'abc 123 222 \n abc 123 222' | awk '{print FNR; print NR;}'
- RS
echo -e 'abc 123 222 * abc 123 222' | awk 'BEGIN{RS="*"} {print FNR}'
通过修改RS为字符串”*“,输入的文本被当做了两行。
- SUBSEP
echo -e 'abc 123 222' | awk '{a[1]=$1; a[2]=$2; a[1,2] = $1$2; print a[1 SUBSEP 2]}'
输出为
abc123
gawk
在GNU中还有一个扩展的awk->gawk,在大致功能上与普通的awk相似,除了以下的几个方面
-
强大的内置函数
gawk内置了十分丰富的函数,所以gawk所能实现的功能比awk要丰富。
举个例子,在调用rand生成随机数的时候采用的是固定的随机序列,我们多次调用下面的命令得到的结果是一样的,
echo 'aa' | awk '{print rand(); print rand(); print rand();}'
如果我们需要打破这个规则使得上面的例子每次都输出不用的结果,就需要使用srand指定seed,比如
echo 'aa' | awk '{srand(23); print rand(); print rand(); print rand();}'
但是这样还是没有从根本上解决问题,因为我们的seed每次是固定的,如果真的想做到随机,就需要每次生成不同的seed,这时候gawk就派上用场了,gawk中内置了systime()函数,该函数会返回从19700101开始的秒数,这样在大多数情况下seed都是不同的,这样也就真的实现了随机。
echo 'aa' | gawk '{srand(systime()); print rand(); print rand(); print rand();}'
-
正则表达式的增强
比如下面的场景使用awk就无法匹配出,而使用gawk就可以
echo "124" | awk '$1 ~ /^\w+$/ {print $1}'
- 内置变量增
-
语法结构
新增了BEGINFILE和ENDFILE表达式,用于在进入文件和离开文件时进行操作。
awk 'BEGINFILE {print "enter file "FILENAME} ENDFILE {print "leave file "FILENAME }' 111 222
输出
enter file 111 leave file 111 enter file 222 leave file 222
- 其他
场景
总的来说awk适用于结构化输出的数据,比如tomcat的access日志,对于这种结果化的数据awk可以很方便的定位到某一列,然后做出某些操作比如打印,统计等。而对于非结构化的数据awk就不那么适合了,如果我们需要的数据不是固定在某一列,那么awk就需要添加很多操作去进行筛选,那么使用awk就有点得不偿失了