AWK教程
通俗的来讲awk更像是一个行列处理器。
awk、sed、grep更适合的方向:
grep 更适合单纯的查找或匹配文本
sed 更适合编辑匹配到的文本
awk 更适合格式化文本,对文本进行较复杂格式处理
基础知识
读(Read)
AWK 从输入流(文件、管道或者标准输入)中读入一行然后将其存入内存中。
执行(Execute)
对于每一行输入,所有的 AWK 命令按顺执行。 默认情况下,AWK 命令是针对于每一行输入,但是我们可以将其限制在指定的模式中。
重复(Repeate)
一直重复上述两个过程直到文件结束。
开始块 BEGIN BLOCK
BEGIN {awk-commands}
类似于java中的static块,在程序启动时执行,且整个过程只执行一次
一般用于初始化一些变量(如分隔符),BEGIN是AWK的关键词,必须大写。
开始块部分是可选的,一个程序可以没有开始块部分。
主体块 BODY BLOCK
/pattern/ {awk-commands}
如上方工作流中展示的那样,每个输入的行都会执行一次主体块的命令。
注意,主体块部分没有关键字
结束块 END BOLCK
END {awk-commands}
结束块是在程序结束时执行的代码。 END 也是 AWK 的关键字,它也必须大写。 与开始块相似,结束块也是可选的。
awk命令行
awk [option] file...
# 打印日志文件
awk '{print}' info.log
$0
此变量表示整个输入记录。
[jerry]$ awk '{print $0}' marks.txt
#执行上面的命令可以得到如下的结果:
1) Amit Physics 80
2) Rahul Maths 90
3) Shyam Biology 87
4) Kedar English 85
5) Hari History 89
$n
此变量表示当前输入记录的第 n 个域,这些域之间由 FS 分割。
[jerry]$ awk '{print $3 "\t" $4}' marks.txt
执行上面的命令可以得到如下的结果:
Physics 80
Maths 90
Biology 87
English 85
History 89
AWK程序文件
除了使用命令外,awk还可以通过读入文本文件中的命令执行
在test.awk文本中写入{print}
执行awk命令
#打印日志文件
awk -f test.awk info.log
基本命令
awk默认分隔符为空格或tab
元素下表从1开始
示例文本
# text
2 this is a test
3 Are you like awk
This's a test
10 There are orange,apple,mongo
awk '{[pattern] action}' {filenames} # 行匹配语句 awk '' 只能用单引号
匹配文本某一列
awk '{print $1,$4}' log.txt #每行按空格或TAB分割,输出文本中的1、4项
#结果
2 a
3 like
This's
10 orange,apple,mongo
# 格式化输出
$ awk '{printf "%-8s %-10s\n",$1,$4}' log.txt
---------------------------------------------
2 a
3 like
This's
10 orange,apple,mongo
通过匹配模式输出某一行
awk '/ABCacheLoader/ {print$0}' info.log
如果某行中含有/parttern/
中的字符串,则该行会被输出。
如果没有主体块——默认的动作是输出记录(行)。因此上面的效果也可以使用下面简略方式实现,它们会得到相同的结果:
awk '/ABCacheLoader/' info.log
通过匹配模式输出某一列
前面我们已经看到了,当模式串匹配成功后, AWK 默认会输出整个记录。不过,我们可以让 AWK 只输出特定的域(列)的内容。 例如,下面的这个例子中当模式串匹配成功后只会输出第1列与第3列的内容:
awk '/ABCacheLoader/ {print$1 "\t" $3}' info.log
其中$1
和$3
的顺序可以随意地调换,即我们能以任意顺序输出各列
计算匹配次数并输出
统计模式串成功匹配文件中行的次数
awk '/OfflineTaskCacheLoader/{++cnt}END{print "count=",cnt}' info.log
上面这个例子中,每次成功的匹配我们都会增加计数器的值,并在结束块中将该计数器的值输出。 请注意,与其它编程语言不一样的地方在于, AWK 在使用一个变量前不需要特意地声明这个变量。
输出字符数大于18的行
awk 'length($0)>18' info.log
AWK 提供了内置的 length 函数。该函数返回字符串的长度。变量 $0 表示整行,缺失的主体块会执行默认动作,例如,打印输出。 因此,如果一行中字符数超过 18, 则比较的结果为真,该行则被输出。
内置变量
ARGC 参数个数
ARGC 表示在命令行提供的参数的个数。
awk 'BEGIN {print "argc = " ,ARGC}' info.log info.log
argc = 3
ARGV 输入参数的数组
这个变量表示存储命令行输入参数的数组。数组的有效索引是从 1 到 ARGC
。其中ARGV[0] = awk
awk 'BEGIN {for (i=0; i<ARGC;i++) {printf "ARGV[%d] = %s\n",i,ARGV[i]}}' one two three
ARGV[0] = awk
ARGV[1] = one
ARGV[2] = two
ARGV[3] = three
ENVIRON 环境变量
获取环境变量
具体的环境变量可以通过系统中的 env 命令查询其它环境变量的名字
awk 'BEGIN{print ENVIRON["PATH"]}'
FILENAME 文件名
值得注意的是在开始块中FILENAME是未定义的
awk 'END{print FILENAME}' info.log
info.log
FS (Field Separator 字段分隔符)
此变量表示输入的数据域之间的分隔符,其默认值是空格。 你可以使用 -F 命令行选项 或者在 BEGIN块中设置分割符 改变它的默认值。
awk 'BEGIN{FS=":"}{print $2}' info.log
#上下等价
awk -F ":" '{print $2}' info.log
awk 'BEGIN{FS=":";print "FS ="FS}' info.log
FS =:
FS和OFS命令
FS:Field Separator,字段分隔符(对输入生效)
OFS:Out of Field Separator,输出字段分隔符(对输出生效)
awk 'BEGIN{ FS=" ";OFS="," }{ print $1,$2,$3,$4 }' file2.txt > file3.txt
NF(Number of Filed)
此变量表示当前输入记录中域的数量。例如,下面这个例子只输出超过两个域的行:
echo -e 激活转义字符。使用-e选项时,若字符串中出现转义字符,则特别加以处理,不会当做一般文字输出
echo -e "one two\nthree\none two three" | awk 'NF >2'
one two three
NR(Number of Row)
该变量记录当前处理的记录的数量。下面例子会输出读入的前2行(NR<3)。
cat info.log|awk 'NR < 3'
#上下等价
awk '{if(NR<3){print}}' info.log
FNR(File Number of Row)
该变量与 NR 类似,不过它是相对于当前文件而言的。此变量在处理多个文件输入时有重要的作用。每当从新的文件中读入时 FNR 都会被重新设置为 0。
awk '{print NR,FNR}' info.log newtable
# 新文件NR继续计数,而FNR重0开始计数
1029 1029
1030 1030
1031 1
1032 2
OFS(Output Field Separator 输出字段分割符)
此变量表示输出域之间的分割符,其默认为空格。
对 {print $1,$2,$3}生效 ,即只对print中的逗号(,)生效
awk 'BEGIN{OFS = "---"}{print $1,$2}' info.log
-----
2020-06-15---20:00:02.446
2020-06-15---20:00:02.457
2020-06-15---20:00:02.462
RS (Row Separator 输入行分隔符)
此变量表示输入记录(行)之间的分隔符,默认为换行符
#以:作为行分割
awk 'BEGIN{RS=":"}{print $1}' info.log | more
-------
2020-06-15
00
02.446
]
49]
00
02.457
]
67]
ORS(行分隔符)
此变量表示输出记录(行)之间的分割符,其默认值是换行符。
awk 'BEGIN{RS=":";ORS="---"}{print $1}' info.log | more
2020-06-15---00---02.446---]---49]---00---02.457---]---67]---00---02.462---]---56]---00---02.468---]
RSTART(匹配字符串)
此变量表示由 match 函数匹配的字符串的第一个字符的位置。
awk 'BEGIN { if (match("One Two Three", "Thre")) { print RSTART } }'
#执行上面的命令可以得到如下的结果:
9
操作符
正则表达式操作符
下面将介绍两种正则表达式操作符:
匹配(Match)
匹配运算符为~。它用于搜索包含匹配模式字符串的域。下面的示例中将输出包括 9 的行:
[jerry]$ awk '$0 ~ 9' marks.txt
执行上面的命令可以得到如下的结果:
2) Rahul Maths 90
5) Hari History 89
不匹配(Not match)
不匹配操作符为 !~。 此操作符用于搜索不匹配指定字符串的域。如下示例输出不包含 9 的行:
[jerry]$ awk '$0 !~ 9' marks.txt
执行上面的命令可以得到如下的结果:
1) Amit Physics 80
3) Shyam Biology 87
4) Kedar English 85
点(Dot)
点字符(.)可以匹配除了行结束字符的所有字符。比如下面的便子就可以匹配 fin, fun, fan 等等。
echo -e "cat\nbat\nfun\nfin\nfan" | awk '/f.n/'
#输出
fun
fin
fan
行开始
行开始符(^)匹配一行的开始。下面的示例将输出所有以字符串 The 开始的行。
echo -e "this is lipengtest" | awk '/^t/'
this is lipengtest
行结束
行结束符($)匹配一行的结束。下面的例子中将输出所有以字符 t 结束的行:
echo -e "this is lipengtest" | awk '/t$/'
this is lipengtest
匹配字符集
匹配字符集用于匹配集合(由方括号表示)中的一个字符。如下例子中,匹配 Call 与 Tall 而不会匹配 Ball。
echo -e "Call\nTall\nBall" | awk '/[CT]all/'
执行上面的命令可以得到如下结果:
fun
fin
fan
排除集
正则匹配时会排除集合中的字符。如下例子中只会输出 Ball。
注意,^符号只有在集合内才会起作用,且如果放在开头作用范围为整个集合
#放在集合开头
echo -e "Call\nTall\nBall" | awk '/[^CT]all/'
执行上面的命令可以得到如下结果:
Ball
#放在集合中间
echo -e "Call\nTall\nBall" | awk '/[C^T]all/'
Call
Tall
#放在集合外,用作行开始
echo -e "Call\nTall\nBall" | awk '/^[CT]all/'
Call
Tall
或
竖线(|)允许正则表达式实现逻辑或运算. 下面例子将会输出 Ball 与 Call 。
echo -e "Call\nTall\nBall\nSmall\nShall" | awk '/Call|Ball/'
执行上面的命令可以得到如下结果:
Call
Ball
最多出现一次
该符号(?)前面的字符不出现或者出现一次
。如下示例匹配 Colour 与 Color。 使用 ? 使得 u 变成了可选字符 。
echo -e "Colour\nColor" | awk '/Colou?r/'
执行上面的命令可以得到如下结果:
Colour
Color
分组
括号用于分组而字符 | 用于提供多种选择。如下的正则表达式会匹配所有包含 Apple Juice 或 Aplle Cake 的行。
[jerry]$ echo -e "Apple Juice\nApple Pie\nApple Tart\nApple Cake" | awk '/Apple (Juice|Cake)/'
#这里的空格是会算进表达式里的
#执行上面的命令可以得到如下结果:
Apple Juice
Apple Cake
控制流
IF-ELSE语句
if-else语句中允许在条件为假时执行另外一组的动作。下面为 if-else 的语法格式:
if (condition)
action-1
else
action-2
其中,条件为真时执行 action-1,条件为假时执行 action-2。下面是使用该语句判断数字是否为偶数的例子:
awk 'BEGIN {num = 11;
if (num % 2 == 0) printf "%d is even number.\n", num;
else printf "%d is odd number.\n", num
}'
执行上面的操作可以得到如下的结果:
11 is odd number.
EXIT
Exit 用于结束脚本程序的执行。该函数接受一个整数作为参数表示 AWK 进程结束状态。 如果没有提供该参数,其默认状态为 0 。下面例子中当和大于 50 时结束 AWK 程序。
awk 'BEGIN {
sum = 0; for (i = 0; i < 20; ++i) {
sum += i; if (sum > 50) exit(10); else print "Sum =", sum
}
}'
Sum = 0
Sum = 1
Sum = 3
Sum = 6
Sum = 10
Sum = 15
Sum = 21
Sum = 28
Sum = 36
Sum = 45
让我们检查一下脚本执行后的返回状态:
echo $?
执行上面的命令可以得到如下的结果:
19
实际例子
筛选出第n个列并输出到文件中
awk 'BEGIN{FS=";"}{print $1 >> "result1.sql"}' result.txt
传入参数并根据参数匹配输出
#!/bin/bash
#########
#
# 目标:查找文件中对应的sql和riskey
# author: lipeng01_dxm
#
#########
# host="10.21.181.103"
# Port=8888
# user="db_user_rw"
# password="EDCIdek25"
# database="mask"
riskey_array=(
"1"
"2"
"3"
"4"
"5"
"6"
)
shell_var="abc"
for ((i=0; i<6; i++)); do
shell_var=${riskey_array[i]}
# awk 'BEGIN{riskey="'$riskey_array[i]'";if($2 == riskey) printf $2}'
awk 'BEGIN{riskey = "'$shell_var'";}{if($2 == riskey) print $0 >> "/Users/dxm/Desktop/result.txt"}' /Users/dxm/Desktop/rieskeyallsql.txt
done
匹配模式输出
awk 'BEGIN{FS=";"}{if($1!~/mdm_per_detail_user_info/) {print "--\n" $1 >> /Users/dxm/Desktop/result2.sql}}' result.txt
文件对比
#在awk中,FNR指的是当前文件中的记录号(通常是行号)并NR引用总记录号。运算符==是一个比较运算符,当两个周围的操作数相等时返回true。
#这意味着条件NR==FNR仅适用于第一个文件,因为FNR每个文件的第一行重置为1,但NR会继续增加。
#此模式通常用于仅对第一个文件执行操作。在next块内是指任何进一步的命令被跳过,所以它们仅在比所述第一其他文件运行。
#该条件FNR==NR比较了相同的两个操作数NR==FNR,因此它的行为方式相同。
awk -F',' 'NR==FNR{a[$1]=$1;next}NR!=FNR{if($1 in a){}else{print $0}}' 5568.csv 5569.csv
awk 'BEGIN{FS = ","}{if(NR==FNR){a[$1]=$1}else{if($1 in a){}else{print $0}}}' 2370.csv 5568.csv
curl命令转换
awk '{print "curl -L -X POST \047//10.157.23.145:8307/mask/_doc/" $1 "\047 -H \047Authorization: Basic ZWxhc3RpYzptYXNrX3Jk\047 -H \047Content-Type: application/json\047 --data-raw \047{}\047"}' 2020-08-12-01.txt > ./2020-08-12-01.sh
sh ./2020-08-12-01.sh
单引号因为是关键字,所以需要使用8进制的\047来代替