一、介绍
awk
是一种编程语言(解释性语言,不需要编译),用于数据截取和报告的工具awk
自动搜索输入的文件,并把每个输入行切分成多个字段(字段:根据分隔符分割成的单元)- 擅长处理数据库和表型数据。适合编写短小一次性程序
centos
默认的awk
使用的是gawk
,系统将awk
通过软链接来指向gawk
- 可以使用新的内置函数和变量
- 在缺省的情况下,它使用扩展的正则表达式
awk
不需要声明变量的数据类型,它内置字符串类型和数值类型
二、命令格式
awk [OPTIONS] [--] program filename_list
选项 | 说明 |
---|---|
-f program -file | 指定包含了awk命令的文件,不是从命令行参数中去读取 可以通过-f选项指定多个包含awk命令文件 |
-F ‘fs’ --field -separator | 指定分隔符即为定义FS变量对应的值 |
-v var=val | 在程序开始之前,将val这个值赋给var这个变量 |
-d[file] | 将全局变量的类型和最终值排序,并将排序好后的结果打印输出到文件中 |
-V | 显示awk的版本 |
三、使用方法及范例
3.1 awk的核心
- PROGRAM——'pattern{action}'
3.2 关于pattern { action }的说明:
pattern
与action
并非同时存在,可以省略其中一个- 如果
action
被省略,默认动作是将每个匹配的行输出 - 如果
pattern
被省略,对于每行都会执行动作
awk基本操作流程:
- 从输入流中读取一行内容,然后使用
pattern{action}
去处理 - 对读取的行进行扫描搜索,搜索行中是否有内容被pattern匹配
- 如果行被
pattern
匹配成功,则执行动作{action}
;如果行中内容不被pattern
匹配,则不执行 - 继续从输入流中读取新的一行,重复上述几个步骤
例一:简单理解awk
问题描述:
file1
文件第一列为员工名字,第二列为时薪,第三列为工作时长- 打印输出员工的名字、时薪和工作时长
执行命令:
[root@CentOS7 commands]# cat file1
Sam 50 8
Jiangle 55 9
Tom 40 6
Tim 60 9
[root@CentOS7 commands]# awk '{print $1,$2,$3}' file1
Sam 50 8
Jiangle 55 9
Tom 40 6
Tim 60 9
说明:
awk
命令后面的程序用单引号引起来,单引号后面的部分为输入文件- 单引号中包围的内容是一个
awk
程序(program),即模式-动作语句(pattern-action)
3.3 pattern详细说明
pattern
,即模式,是决定awk
程序中定义的动作action是否能够被执行的关键pattern
支持的规则:正则表达式,字符串与数字比较,流程控制语句
例二:pattern——控制输出条件1
问题描述:
file1
文件第一列为员工名字,第二列为时薪,第三列为工作时长- 打印输出时薪大于等于50的员工名字和总薪资
执行命令:
[root@CentOS7 commands]# cat file1
Sam 50 8
Jiangle 55 9
Tom 40 6
Tim 60 9
[root@CentOS7 commands]# awk '$2>=50 {print $1,$2*$3}' file1
Sam 400
Jiangle 495
Tim 540
说明:
- 其中模式
pattern
为'$2>=50'
,表示扫描每一个输入的行,如果第二列大于0,则执行动作action
,否则不执行action
- 动作
action
为'{ print $1,$2*$3 }'
,模式匹配成功后执行的对应动作,该动作用于打印输出第一个字段和第二、三字段的乘积
模式表示方式汇总:
模式 | 说明 |
---|---|
/regexp/ | 正则之字符串匹配模式/regexp/(拓展的正则表达式) 例如:expression ~ /regexpr/表示当前输入行包含能被regexpr匹配的子字符串时,该模式被匹配 |
relational expression | 当表达式拥有一个数值形式的值,运算符要求一个字符串值,则awk会将该数值自动转换成字符串 当表达式拥有一个字符串形式的值,运算符要求一个数值,则awk会将该字符串值自动转换成数值 关于表达式真与假的说明:如果一个表达式,对当前述入行的求值结果非零或不为空,那么该行就被匹配 如果比较字符串,比较时是逐字符依赖ASCII字符表比较 |
pattern 1 && pattern2 | 只有当pattern1和pattern2都匹配了,才会执行action |
pattern1 || pattern2 | 当pattern1和pattern2其中一个匹配了,就会执行action |
比较运算符汇总
运算符 | 说明 |
---|---|
< | 小于 |
<= | 小于等于 |
== | 等于 |
!= | 不等于 |
> | 大于 |
>= | 大于等于 |
~ | 匹配 |
!~ | 不匹配 |
表达式运算符汇总(也可以用在action)
操作 | 运算符 | 举例 |
---|---|---|
赋值 | =、+=、-=、*=、/=、^= | sum+=i ,即sum=sum+i |
条件表达式 | ?: | x?y:z ,若x为真则为y,否则为z |
逻辑或 | || | x\|\|y ,x或y为真,则表达式为真 |
逻辑与 | && | x&&y ,x与y为真,则表达式为真 |
数组成员 | in | i in a ,i在a中 |
字段 | $ | $1 表示输入行的第一个字段 |
组合 | ( ) | $(2+1) 表示第三个字段 |
匹配 不匹配 | ~ !~ | |
自增 自减 | ++ – |
例三:pattern——控制输出条件2
问题描述:
- 打印输出
file2
第一和二个字段都为数字或第三和四个字段都为字母的行
执行命令:
[root@CentOS7 commands]# cat file2
1 2 3 4
1 2 a b
a 1 4 3
1 a 2 b
[root@CentOS7 commands]# awk '($1~/[0-9]/ && $2~/[0-9]/)||($3~/[a-zA-Z]/ && $4~/[a-zA-Z]/)' file2
1 2 3 4
1 2 a b
说明:
$1~/[0-9]/
表示匹配第一列中包含数字0-9,其中的pattern
为[0-9]$3~/[a-zA-Z]/
表示匹配任意大小写字母
3.4 action详细说明
关键字和变量
- 关键字
关键字 | 说明 |
---|---|
BEGIN | 放在程序开头,当awk 从输入流中读取数据之前,BEGIN 语句开始执行(初始化),可以执行多个BEGIN |
END | 放在程序末尾,当所有输入流被读取完毕,END 语句开始执行(扫尾),可以执行多个END |
- 内建变量
变量 | 说明 |
---|---|
$n | 当前记录的第n个字段,字段间由FS分隔 |
$0 | 完整的输入记录 |
FS | Field Separator,表示字符分隔符,即读取输入流时,以FS作为字段分隔的依据,默认为空格符 |
RS | Record Separator,表示记录分隔符,即读取输入流时,以RS作为记录分隔的依据,默认是一个换行符 |
OFS | Output Field Separator,表示输出字段分隔符,默认为空格符 |
ORS | Output Record Separator,表示输出记录分隔符,默认为换行符 |
NF | Number of Field,表示每一行的字段数目 |
NR | Number of Row,记录读取的行的行号 |
IGNORECASE | IGNORECASE如果为真,则进行忽略大小写的匹配 |
例四:action——控制语句输出1
问题描述:
file1
文件第一列为员工名字,第二列为时薪,第三列为工作时长- 打印输出每一个员工名字和总薪资,添加一个表头说明,中间用":"隔开
执行命令:
[root@CentOS7 commands]# cat file1
Sam 50 8
Jiangle 55 9
Tom 40 6
Tim 60 9
[root@CentOS7 commands]# awk 'BEGIN{OFS=":";ORS="\n\n"} BEGIN{print "name:total_salary"} {print $1,$2*$3}' file1
name:total_salary
Sam:400
Jiangle:495
Tom:240
Tim:540
说明:
- 第一个
BEGIN
语句是控制输出字段分隔符OFS
和输出记录分隔符ORS
- 第二个
BEGIN
语句是输出字符串"name:total_salary"
- 执行完两个BEGIN后,
awk
再从输入流中读取处理数据 - 注意:对变量
FS
、OFS
和ORS
赋值时,需要使用双引号,不能没有引号或使用单引号
格式化输出语句
action
的格式化输出语句通过printf
实现,在print
语句中,多个变量之间使用逗号作为分隔符,对变量进行分隔
- printf语法格式:
printf(format,expression1,expression2…)
- 参数
format
包含“%”、对齐方式、格式控制字符、字符串最大宽度
printf格式控制字符
格式字符 | 说明 |
---|---|
- | 左对齐修饰符,默认为右对齐 |
# | 显示8 进制整数时在前面加个0 显示16 进制整数时在前面加0x |
+ | 显示使用d 、e 、f 和g 转换的整数时,加上正负号± |
0 | 用0而不是空白符来填充所显示的值,即用字符’0’填充 |
%c | 表示一个ASCII字符 |
%s | 表示一个字符串 |
%d | 表示一个十进制整数 |
%u | 表示一个无符号的十进制数 |
%x | 表示一个十六进制数 |
%o | 表示一个八进制数 |
%f | 表示一个浮点数 |
%e | 用科学计数法(e 记数法),表示一个浮点数 |
%g | 选择e或f中较短的一种形式 |
上面差不多就是printf
的常用格式输出了,下面是一些常用的复合。就是啊,附和!!!
复合格式字符 | 说明 |
---|---|
%10d | 十进制数占10列 |
%010d | 十进制数占10列,前面用字符’0’填充 |
%+10d | 十进制数占10列,包含数值的符号(±) |
%#10x | 十六进制数占10列,显示前缀0x |
%-10.3f | 浮点数左对齐占10列,保留3位小数 |
%0+10.3f | 浮点数右对齐占10列,保留3位小数,显示符号,左补齐填充字符’0’ |
%10s | 字符串右对齐,占10列 |
%-10s | 字符串左对齐,占10列 |
例五:action——控制语句输出2
问题描述:
file1
文件第一列为员工名字,第二列为时薪,第三列为工作时长- 打印输出每一个员工编号(所在行数即为编号)、名字和总薪资;员工编号左对齐占5列,名字右对齐占10列,总薪资占10列右对齐保留两位小数
执行命令:
[root@CentOS7 commands]# cat file1
Sam 50 8
Jiangle 55 9
Tom 40 6
Tim 60 9
[root@CentOS7 commands]# awk 'BEGIN{printf "%-5s%10s%10s\n","ID","NAME","SALARY"}{printf("%-5d%10s%10.2f\n",NR,$1,$(NF-1)*$NF))}' file1
ID NAME SALARY
1 Sam 400.00
2 Jiangle 495.00
3 Tom 240.00
4 Tim 540.00
说明:
NF
表示当前行的字段数(列数),$NF
表示当前行的最后一个字段值- 这里的
$(NF-1)*$NF
与$2*$3
相同 printf
不会自动换行,需要添加换行符"\n"
才能换行
四、awk进阶
4.1 输出重定向
4.1.2 输出重定向到文件
- 使用
>>
重定向,不清空文件内容,将内容追加到文件尾部 - 使用
>
重定向,清空文件内容,再把内容写入文件
例六:输出重定向到文件
问题描述:
file1
文件第一列为员工名字,第二列为时薪,第三列为工作时长- 将时薪大于等于50的员工的信息保存到
new_file1
中
执行命令:
[root@CentOS7 commands]# cat file1
Sam 50 8
Jiangle 55 9
Tom 40 6
Tim 60 9
[root@CentOS7 commands]# awk '$2>=50 {print $0 > "new_file1"}' file1
[root@CentOS7 commands]# cat new_file1
Sam 50 8
Jiangle 55 9
Tim 60 9
说明:
- 重定向时,文件名必须要用双引号引起来,否则会被当作一个未初始化的变量
>
重定向时,会覆盖目标文件的内容
4.1.2 输出重定向到管道
- 使用管道命令
|
进行数据的处理
例七:输出重定向到管道
问题描述:
file1
文件第一列为员工名字,第二列为时薪,第三列为工作时长- 按照总薪资降序进行排序,名字占10列左对齐,时薪占10列左对齐,工作时长占10列左对齐,总薪资占10列左对齐
执行命令:
[root@CentOS7 commands]# cat file1
Sam 50 8
Jiangle 55 9
Tom 40 6
Tim 60 9
[root@CentOS7 commands]# awk '{printf("%-10s%-10d%-10d%-10d\n",$1,$2,$3,$2*$3) | "sort -nrk 4 "}' file1
Tim 60 9 540
Jiangle 55 9 495
Sam 50 8 400
Tom 40 6 240
说明:
- 语句
{printf(...) | "sort..."}
中的sort
必须用双引号引起来 sort
中-n
表示按照字符串的数值顺序比较;-r
表示逆序比较,默认为升序;-k 4
表示以第4个字段(第4列)为关键字进行比较- 也可以将
awk
的格式化输出通过管道传递给sort
命令,也能实现排序并格式化输出,等价命令为:awk '{printf("%-10s%-10d%-10d%-10d\n",$1,$2,$3,$2*$3)}' file1 | sort -nrk 4
4.2 流程控制
流程控制语句中,if-else
用于决策,while
、for
、do whlie
用于循环,break
、continue
用于控制循环
4.2.1 if-else语句
语法结构
{
if (expression)
statements
else
statements
}
或者
{if (expression) statements;else statements}
例八:简单使用if-else语句
[root@CentOS7 commands]# cat file1
Sam 50 8
Jiangle 55 9
Tom 40 6
Tim 60 9
[root@CentOS7 commands]# cat if.awk
#!/bin/bash
{
if($2>=50)
printf("%s has a good salary!\n",$1)
else
printf("%s need to change a job!\n",$1)
}
[root@CentOS7 commands]# awk -f if.awk file1
Sam has a good salary!
Jiangle has a good salary!
Tom need to change a job!
Tim has a good salary!
说明:
awk -f
可以指定文件作为pattern{action}
,上述例子并未指定pattern
- 从上述例子可以看出,时薪小于50的,就可以换工作了(hahaa,偶尔皮一下🐶)
4.2.2 while语句
语法结构
{
while (expression)
{
statement1
statement2
...
}
}
或者
{while (expression){statement1;statement2;...}}
例九:简单的使用while语句
问题描述:
- 将
data
文件中的数按行求和并输出
执行命令:
[root@CentOS7 commands]# cat data
1 2 3 4
5 6 7 8 9
6 6 6 6 6 6
10 20 30 40 50
[root@CentOS7 commands]# cat while.awk
#!/bin/bash
{
i=1
sum=0
while(i<=NF)
{
sum+=$i
i++
}
printf("Line %d, sum is %d\n",NR,sum)
}
[root@CentOS7 commands]# awk -f while.awk data
Line 1, sum is 10
Line 2, sum is 35
Line 3, sum is 36
Line 4, sum is 150
说明:
- 每从输入流读取一行,
while
循环就会被执行 - 当
expression
的值为真时,进入while
循环
4.2.3 for语句
语法结构
{
for(expresion1;expression2;expression3)
{
statement1
statement2
}
}
或者
{ for(expresion1;expression2;expression3){statement1;statement2;...}}
expression1
:进入for
循环之前执行一次该语句。一般用于对变量的初始化expression2
:每执行一次循环后,都会执行一次该语句,如果结果为真,则继续循环,反之退出循环。一般用于循环的条件判断expression3
:每执行一次循环后,都会执行一次该语句。一般用于做变量变化
例十:简单的使用for语句
问题描述:
- 将
data
文件中的数按行求和并输出
执行命令:
[root@CentOS7 commands]# cat data
1 2 3 4
5 6 7 8 9
6 6 6 6 6 6
10 20 30 40 50
[root@CentOS7 commands]# cat while.awk
#!/bin/bash
{
sum=0
for(i=1;i<=NF;i++)
sum+=$i
printf("Line %d, sum is %d\n",NR,sum)
}
[root@CentOS7 commands]# awk -f for.awk data
Line 1, sum is 10
Line 2, sum is 35
Line 3, sum is 36
Line 4, sum is 150
循环语句里面也可以使用break
、continue
语句,具体的使用这里就不再赘述了。
我的🐴🦆,8k+字!!!第一次总结这么多,累辽~~