awk 'BEGIN { commands } PATTERN { commands } END { commands }'
begin块 body块 end块
执行流程:
1)先执行begin块。
相当于循环初始化。
2)对每条输入记录,执行body块。
相当于执行循环体。
如果是处理多行文本,默认用换行符拆分记录,即循环每行进行处理。
3)对每条记录(每行文本)的默认处理:
用分隔符(默认是空格)分隔每行中的字段,分别赋值给$1, $2 … ($0代表整行)
根据需要对各个字段进行处理,输出等。
4)循环结束后,执行end块。
pattern模式:
1)/正则表达式/:/some string/
2)关系表达式:$2>10 NR%2==0
3)模式匹配表达式: ~ ~!
4)范围区间:
'NR==1, NR==10'
1-10行
$ cat /tmp/test.log
2023-05-18 05:08:56.965846 460 4936 I SOMEIP : 2023-05-18 05:08:56.871641 [info] [TID:571] repetitionsBaseDelay_ = 100
2023-05-18 05:08:56.965863 460 4936 I SOMEIP : 2023-05-18 05:08:56.871645 [info] [TID:571] repetitionsMax_ = 3
2023-05-18 05:08:30.896556 1538 1538 I SettingsIFImpl: line 1
2023-05-18 05:08:30.896556 1538 1538 I SettingsIFImpl: line 2
# 包含SOMEIP的行
$ awk '/SOMEIP/' /tmp/test.log
2023-05-18 05:08:56.965846 460 4936 I SOMEIP : 2023-05-18 05:08:56.871641 [info] [TID:571] repetitionsBaseDelay_ = 100
2023-05-18 05:08:56.965863 460 4936 I SOMEIP : 2023-05-18 05:08:56.871645 [info] [TID:571] repetitionsMax_ = 3
# 不包含SOMEIP的行
$ awk '!/SOMEIP/' /tmp/test.log
2023-05-18 05:08:30.896556 1538 1538 I SettingsIFImpl: line 1
2023-05-18 05:08:30.896556 1538 1538 I SettingsIFImpl: line 2
# 把字段$6匹配模式的行,打印每行中$6后续内容
$ awk 'BEGIN{ ORS=" " } $6~/SOMEIP/{ for(i=8; i<=NF; i++) { print $i } print "\n" }' /tmp/test.log
2023-05-18 05:08:56.871641 [info] [TID:571] repetitionsBaseDelay_ = 100
2023-05-18 05:08:56.871645 [info] [TID:571] repetitionsMax_ = 3
2023-05-18 05:08:56.871641 [info] [TID:571] repetitionsBaseDelay_ = 100
2023-05-18 05:08:56.871645 [info] [TID:571] repetitionsMax_ = 3
内置变量
FILENAME: 当前文件名
NR: 表示所有处理文件已处理的输入记录个数
FNR: 文件的当前记录数
NF: 表示数据文件中数据字段的个数,可以通过$NF获取最后一个数据字段
ARGC: 命令行参数个数
ARGV: 命令行参数数组
$0: 这个变量包含执行过程中当前行的文本内容。
$n: 一行记录的第n个字段,例如$1, $2
FS:输入字段分隔符
OFS:输出字段分隔符
RS:输入记录分割符
ORS:输出字段分隔符
FIELDWIDTHS:定义数据字段的宽度
$ awk '{print FILENAME, NF, $0}' /tmp/test.log
/tmp/test.log 8 2023-05-18 05:08:30.896556 1538 1538 I SettingsIFImpl: line 1
/tmp/test.log 8 2023-05-18 05:08:30.896556 1538 1538 I SettingsIFImpl: line 2
$ awk '{print FILENAME "line>>" NR, $0}' /tmp/test.log
/tmp/test.logline>>1 2023-05-18 05:08:30.896556 1538 1538 I SettingsIFImpl: line 1
/tmp/test.logline>>2 2023-05-18 05:08:30.896556 1538 1538 I SettingsIFImpl: line 2
body块:
/pattern/ { commands }
1)匹配正则表达式pattern的记录,执行命令commands。
2)可以有多个匹配模式,例如:
(1)当匹配pattern1时,执行commands1。如果匹配pattern2就执行commands2.
(2)next作用就是跳过后面的模式匹配和命令(类似if/else的关系);如果没有next命令,则每个模式匹配都会进行判断。
/patten1/ {commands1; next} /pattern2/ {commands2}
3)如果省略{commands},默认执行print $0。只执行/pattern/匹配过滤。
(1)对于简单字符串,退化成grep。
(2)利用内置变量和表达式做复杂查找(筛选),例如筛选出偶数行。
$ awk '/Max/' /tmp/test.log
$ awk 'NR%2==0' /tmp/test.log
4)print函数在commands中可以重定向输出到文件或者管道
awk 'NR%2==0 { print $1 > "/tmp/part.log" }' /tmp/test.log
各个命令块:
1)可编写多条语句。每行一个命令,结尾不必有分号。也可以分号分隔的多条命令。
2)支持if-else,for,while等控制结构。
3)支持数据,索引可用数字或字符串,相当于map。
4)可自定义函数
function find_min(num1, num2) { if (num1 < num2) return num1 return num2 }
5)内置函数:
(1)数学函数:sin, cos, log, sqrt, int, rand
(2)字符串函数:gsub, sub, substr, index, length, match, split, tolower, toupper, sprintf, strtonum
sub(reg, str [, target])
a) 匹配reg的字符串,替换为str。
b) target为替换目标字符串,默认为 0 ,可指定为某个表里或者字段 0,可指定为某个表里或者字段 0,可指定为某个表里或者字段n。
c) gsub和sub原型一样,替换所有匹配reg的字符串,sub只替换第一个出现。
gensub(reg, str, h [, target])
a) h可指定替换第几个出现的reg,或者“g/G”标识替换全部。
b) 在str中可以通过“\n”引用reg出现的位置。
print
(3)时间函数:mktime, strftime, systime
(4)位操作函数:and, or, xor, compl, lshift, rshift
(5)其他函数:close, flush, exit, delete, getline, next, nextfile, return system
# sub,gsub例子
# sub
$ cat /tmp/test.log
2023-05-18 05:08:56.965846 460 4936 I SOMEIP : 2023-05-18 05:08:56.871641 [info] [TID:571] client_timer_18215_1 repetitionsBaseDelay_ = 100
2023-05-18 05:08:56.965863 460 4936 I SOMEIP : 2023-05-18 05:08:56.871645 [info] [TID:571] client_timer_18215_1 repetitionsMax_ = 3
#sub函数只替换第一次出现的字符串
$ awk '{ sub(/2023-05-18/, "1234-56-78"); print $0 }' /tmp/test.log
1234-56-78 05:08:56.965846 460 4936 I SOMEIP : 2023-05-18 05:08:56.871641 [info] [TID:571] client_timer_18215_1 repetitionsBaseDelay_ = 100
1234-56-78 05:08:56.965863 460 4936 I SOMEIP : 2023-05-18 05:08:56.871645 [info] [TID:571] client_timer_18215_1 repetitionsMax_ = 3
#gsub函数替换一行中所有匹配的字符串
$ awk '{ gsub(/2023-05-18/, "1234-56-78"); print $0 }' /tmp/test.log
1234-56-78 05:08:56.965846 460 4936 I SOMEIP : 1234-56-78 05:08:56.871641 [info] [TID:571] client_timer_18215_1 repetitionsBaseDelay_ = 100
1234-56-78 05:08:56.965863 460 4936 I SOMEIP : 1234-56-78 05:08:56.871645 [info] [TID:571] client_timer_18215_1 repetitionsMax_ = 3
# gsub删除收尾空格
$ awk '{gsub(/^ +| +$/,"")} {print "=" $0 "="}' onefile.txt
用多个分隔符拆分记录:
$ cat test.log
2023-05-18 05:08:56.965846 460 4936 I SOMEIP
# 用空格、.、:多个分隔符来拆分记录,默认只用空格分隔符
$ awk -F "[ .:]" '{print $1,$2,$3,$4,$5}' test.log
2023-05-18 05 08 56 965846
# FS和-F参数等效
$ awk 'BEGIN{FS="[ .:]"} {print $1,$2,$3,$4,$5}' test.log
2023-05-18 05 08 56 965846
自定义变量
1)对脚本比较有用,命令行中用处不大。
2)在命令行中,自定义变量写在脚本指令之后。脚本指令紧挨着awk。
$ awk '{ print name"="age }' name=tom age=12 /tmp/test.log
tom=12
tom=12
数组:
1)关联数组,map类型。
2)不用先定义
3)for…in循环可能无序,for (i=1…)循环有序
$ awk '
BEGIN {
str="this is a string";
len=split(str, array, " ");
print length(array), len;
for (i in array)
print i": "array[i];
}'
4 4
1: this
2: is
3: a
4: string
$ awk
'BEGIN {
str="this is a string";
len=split(str, array, " ");
print length(array), len;
for (i=1; i<=len; i++)
print i": "array[i];
}'
4 4
1: this
2: is
3: a
4: string
$ awk
'BEGIN{
arr["one"]=1;
arr["two"]=2;
arr["three"]=3;
for (item in arr)
print item"->"arr[item]
}'
three->3
two->2
one->1
流程控制:
$ cat /tmp/file.txt
line 1
line 2
line 3
line 4
line 5
line 6
# if
$ awk '{
if (NR % 2 == 0)
{
print $0
}
else if (NR %3 == 0)
{
print $0
}
}' /tmp/file.txt
line 2
line 3
line 4
line 6
# while 循环
$ awk 'BEGIN{
count=3;
while (count>0)
{
print count;
count--;
}
}' /tmp/file.txt
3
2
1
# for循环
$ awk 'BEGIN{
for(count=3; count>0; count--)
{
print count;
}
}' /tmp/file.txt
3
2
1
控制命令:
break,退出while/for循环
continue,继续下一次循环
next,继续系一条记录,把body块作为循环体,next类似continue,跳到下一条记录。
exit, 在body块中exit,结束body块循环,执行END;在END中exit,退出程序。包body块作为循环体,exit类似break。
数值计算例子:
$ cat /tmp/num.txt
1
2
3
4
5
# 计算均值
$ awk 'BEGIN{ sum=0; } { sum+=$1; } END{ print sum/NR }' /tmp/num.txt
3
$ cat /tmp/num.txt
1 2 3 4 5
11 22 33 44 55
10 20 30 40 50
# 计算均值
$ awk '{ sum=0; for(i=1; i<=NF; i++) sum+=$i; print sum/NF }' /tmp/num.txt
3
33
30
awk可以自定义函数,暂时用不到,先不记录了。