一. 基本介绍

awk 是一门特殊的编程语言, 它非常适合处理一些任务(改变数据格式、验证数据的有效性、搜索特定的数据项、求和、打印报表等), 经常只需要一两行便可搞定。

一个 awk 程序由一系列的模式和动作组成, 这些模式与动作说明了在输入中搜索哪些数据, 以及当符合条件的数据被找到时, 应该执行什么操作。

awk 在输入文件集合中搜索与模式相匹配的输入行, 当找到一个匹配行时, 便会执行对应的动作。通过字符串, 数值, 字段, 变量和数组元素的比较操作, 再加上正则表达式, 利用这些组合, 一个模式可以用来选择输入行, 而动作可以对选中的行作任意的处理。

二. awk 的语法

1. 命令行格式

2. 程序基本结构

awk 命令中的程序格式就是一个或多个这样的格式组成的:pattern { action }

awk 的基本操作是在由输入行组成的序列中, 陆续地扫描每一行, 搜索可以被模式(pattern)匹配的行。

每匹配一个模式, 对应的动作 (action)(可能包含多个步骤) 就会执行 。

例子:

$3 == 19 { print $1 }

建立一个文本文件:

# cat awk_demo
1 Adam 18 Boston
2 Bob 17 Charlotte
3 Chris 17 Dallas
4 David 19 Houston
5 Ellis 20 Illinois

如果只有模式,则会打印匹配这个模式的一整行:$3 == 19

# awk '$3==19' awk_demo
4 David 19 Houston

如果只有动作,对于每一个输入行,动作都会执行:{ print $1 }

# awk '{ print $1 }' awk_demo
1
2
3
4
5

运行完整命令的效果如下所示:

# awk '$3==19 { print $1 }' awk_demo
4

3. 正则表达式

在模式匹配时使用,具体规则请看神奇的正则表达式_yspg_217的博客-CSDN博客

4. 模式(pattern)的格式

expression { statements }

每碰到一个使 expression 为真的输入行, statements 就执行。expression 为真指的是其值非零或非空

/regular expression/ { statements }

当碰到这样一个输入行时, statements 就执行: 输入行含有一段字符串, 而该字符串可以被正则匹配

compound expression { statements }

一个复合模式将表达式用 &&(AND), ||(OR), !(NOT), 以及括号组合起来; 当 compound pattern 为真时, statements 执行

pattern1, pattern2 { statements }

一个范围模式匹配多个输入行, 这些输入行从匹配 pattern1 的行开始, 到匹配 pattern2 的行结束 (包括这两行), 对这其中的每一行执行 statements

BEGIN { statements }

在输入被读取之前, statements 执行一次

END { statements}

当所有输入被读取完毕之后, statements 执行一次

5. 两种特殊的模式(BEGIN END)

BEGIN 与 END 这两个模式不匹配任何输入行。

当 awk 从输入读取数据之前, BEGIN 的语句开始执行; 当所有输入数据被读取完毕, END 的语句开始执行。于是, BEGIN 与 END 分别提供了一种控制初始化与收尾的方式。

BEGIN 与 END 不能与其他模式作组合。如果有多个 BEGIN, 与其关联的动作会按照它们在程序中出现的顺序执行, 这种行为对多个 END 同样适用。通常将 BEGIN 放在程序开头, 将 END 放在程序末尾。

BEGIN 与 END 是唯二的不能省略动作的模式。

6. 动作(action)的格式

有时候动作会非常简单: 一条单独 的打印语句或赋值语句。

在有些时候, 动作有可能是多条语句, 语句之间用换行符或分号分开。

参数类型:

常量:字符串,数值

变量:自己定义

内建变量:awk 系统原生的变量

字段变量:$1, $2, … , $NF(NF表示当前行的字段的数目)

7. 内建变量

首先介绍两个重要的概念,字段(Field)和记录(Record):

记录(Record):默认一行内容代表一个记录

字段(Field):一个记录包括多个字段

内建变量 意义 默认值
ARGC 命令行参数的个数
ARGV 命令行参数数组
FILENAME 当前输入文件名
FNR 当前输入文件的记录个数
FS 控制着输入行的字段分隔符 " "
NF 当前记录的字段个数
NR 到目前为止读取的记录数量
$0 当前行的全部内容
$n 当前行第n个字段的内容
OFMT 数值的输出格式 "%.6g"
OFS 输出字段分隔符 " "
ORS 输出记录分隔符 "\n"
RLENGTH 被函数 match 匹配的字符串长度
RS 控制着输入行记录的分隔符 "\n"
RSTART 被函数 match 匹配的字符串的开始
SUBSEP 下标分隔符 "\034"
# awk '{print ARGC}' awk_demo
2
2
2
2
2# awk '{print ARGV[ARGC-2]}' awk_demo
awk
awk
awk
awk
awk# awk '{print ARGV[ARGC-1]}' awk_demo
awk_demo
awk_demo
awk_demo
awk_demo
awk_demo# awk '{print FILENAME}' awk_demo
awk_demo
awk_demo
awk_demo
awk_demo
awk_demo# awk '{print FNR}' awk_demo
1
2
3
4
5# awk -v FS=" " '{print $2}' awk_demo
Adam
Bob
Chris
David
Ellis# awk '{print NF}' awk_demo
4
4
4
4
4# awk '{print NR}' awk_demo
1
2
3
4
5# awk 'BEGIN {OFS=":"} {print $1,$2}' awk_demo
1:Adam
2:Bob
3:Chris
4:David
5:Ellis# awk 'BEGIN {OFS=":"} {print $1 $2}' awk_demo
1Adam
2Bob
3Chris
4David
5Ellis# awk 'BEGIN {OFS=":"; ORS="\t"} {print $1,$2}' awk_demo
1:Adam  2:Bob   3:Chris 4:David 5:Ellis# awk 'BEGIN {RS=" "} {print}' awk_demo
1
Adam
18
Boston
2
Bob
17
Charlotte
3
Chris
17
Dallas
4
David
19
Houston
5
Ellis
20
Illinois

8. 运算符与代数函数

运算符
+
-
*
/
% 取余
^或** 乘方
++ 加1
-- 减1
+= a+=b 等价于 a=a+b
-= a-=b 等价于 a=a-b
*= a*=b 等价于 a=a*b
/= a/=b 等价于 a=a/b
%= a%=b 等价于 a=a%b
^= a^=b 等价于 a=a^b
< 小于
> 大于
<= 小于等于
>= 大于等于
== 等于
!= 不等于
~ 匹配
!~ 不匹配
|| 逻辑或
&& 逻辑与
! 逻辑非
代数函数
sin(x) x的正弦
cos(x) x的余弦
exp(x) 自然对数的x次方
int(x) x取整
log(x) ln(x)
sqrt(x) x的开方
atan2(y,x) arctan(y/x),取值范围是-pi~pi
rand() 伪随机数[0,1)
srand() x设置为新的种子,返回旧种子的值,如果没有x,就用当天时间

实例函数:

# cat awk_s.awk
NR==1{print $1"加"$2 "=" $1+$2print $1"减"$2"=" $1-$2print $1"乘"$2"=" $1*$2print $1"除以"$2"=" $1/$2print $1"对"$2"取余=" $1%$2print $1"的"$2"次方=" $1^$2print $1"的自增=" $1++print $1"的自减=" $1--print "自然对数的"$1"次幂=" exp($1)print $3"取整=" int($3)print $1"的对数=" log($1)print $1"的开方=" sqrt($1)print $2/$1"的arctan值=" atan2($2,$1)print "随机数=" rand()print "旧的种子值=" srand($3)
}
NR>1{print $1"的正弦=" sin($1)print $1"的余弦=" cos($1)
}

示例文本文件:

# cat awk_demo2
2 1 3.14
0
15
30
45
60
90

执行结果:

# awk -f awk_s.awk awk_demo2
2加1=3
2减1=1
2乘1=2
2除以1=2
2对1取余=0
2的1次方=2
2的自增=2
3的自减=3
自然对数的2次幂=7.38906
3.14取整=3
2的对数=0.693147
2的开方=1.41421
0.5的arctan值=0.463648
随机数=0.237788
旧的种子值=1
0的正弦=0
0的余弦=1
15的正弦=0.650288
15的余弦=-0.759688
30的正弦=-0.988032
30的余弦=0.154251
45的正弦=0.850904
45的余弦=0.525322
60的正弦=-0.304811
60的余弦=-0.952413
90的正弦=0.893997
90的余弦=-0.448074

9. 输出方法(print printf)

重点介绍一下printf:

格式为:printf( format-expression [, arguments] )

format-expression 中的格式控制符:

fmt $1 printf("fmt",$1)
%c 98 b
%d 98.5 98
%5d 98.5    98
%e 98.5 9.850000e+01
%f 98.5 98.500000
%7.2f 98.5   98.50
%g 98.5 98.5
%.6g 98.5 98.5
%o 98 142
%06o 98 000142
%x 98 62
%X 98 62
|%s| "Feburary" |Feburary|
|%10s| Feburary |  Feburary|
|%-10s| Feburary |Feburary  |
|%.3s| Feburary |Feb|
|%10.3s| Feburary |       Feb|
|%-10.3s| Feburary |Feb       |
%% Feburary %

%c ASCII字符

%d 十进制数

%e 浮点数([-]d.precisione[+-]dd)

%f 浮点数([-]ddd.precision)

%g %e或%f的变形,去掉结尾的零

%o 无符号八进制数

%s 字符串

%x 无符号十六进制数,a-f表示9-15

%X 无符号十六进制数,A-F表示9-15

%% 百分号

% 与控制字符之间可以出现额外的参数:

- 表达式在它所处的域中左对齐

width        当需要时, 把域的宽度填充到该值, 前导的 0 表示用 0 填充

.prec        字符串最大宽度, 或小数点后保留的位数

转义字符:

\a 警告

\b 退格

\f 换页符

\n 换行符

\r 回车

\t 水平制表

\v 垂直制表

\ddd 1~3位的8进制数

\xhex 16进制数

\c 任意字符

# awk '{print $2, "comes from", $4}' awk_demo
Adam comes from Boston
Bob comes from Charlotte
Chris comes from Dallas
David comes from Houston
Ellis comes from Illinois

10. 内建函数

gsub(r,s) 将$0中所有r替换为s,返回替换次数
gsub(r,s,t) 将t中所有r替换为s,返回替换次数
index(s,t) 返回t在s中第一次出现的位置,没出现返回0
length(s) 返回s包含的字符个数
match(s,r) 测试s是否包含r匹配的子串,返回子串的起始位置或0;设置RSTART和RLENGTH
split(s,a) 用FS将s分割到数组a中,返回字段的个数
split(s,a,fs) 用fs将s分割到数组a中,返回字段的个数
sprintf(fmt,expr-list) 根据格式字符串fmt返回格式化后的expr-list
sub(r,s) 将$0的最左最长的、能被r匹配的子字符串替换为s,返回替换次数
sub(r,s,t) 将t的最左最长的、能被r匹配的子字符串替换为s,返回替换次数
substr(s,p) 返回s中从位置p开始的后缀
substr(s,p,n)

返回s中从位置p开始,长度为n的子字符串

toupper(s)

变大写,返回变化后的字符串

tolower(s)

变小写,返回变化后的字符串

system() 使用 shell 命令
# awk '{gsub(/s/,"t");print}' awk_demo
1 Adam 18 Botton
2 Bob 17 Charlotte
3 Chrit 17 Dallat
4 David 19 Houtton
5 Ellit 20 Illinoit# awk '{print index($0,"s")}' awk_demo
13
0
7
15
7# awk '{print length($0)}' awk_demo
16
18
17
18
19# awk '{print match($0, "s"),RSTART,RLENGTH}' awk_demo
13 13 1
0 0 -1
7 7 1
15 15 1
7 7 1# awk '{print split($0,a),a[1],a[2],a[3],a[4]}' awk_demo
4 1 Adam 18 Boston
4 2 Bob 17 Charlotte
4 3 Chris 17 Dallas
4 4 David 19 Houston
4 5 Ellis 20 Illinois# awk '{print sprintf("%-10s",$2)}' awk_demo
Adam
Bob
Chris
David
Ellis# awk '{print sub(/s/, "t");print}' awk_demo
1
1 Adam 18 Botton
0
2 Bob 17 Charlotte
1
3 Chrit 17 Dallas
1
4 David 19 Houtton
1
5 Ellit 20 Illinois# awk '{print substr($0,12)}' awk_demo
oston
arlotte
Dallas
Houston
Illinois# awk '{print substr($0,12,5)}' awk_demo
oston
arlot
Dalla
Houst
Illin# awk '{print toupper($4)}' awk_demo
BOSTON
CHARLOTTE
DALLAS
HOUSTON
ILLINOIS# awk '{print tolower($4)}' awk_demo
boston
charlotte
dallas
houston
illinois# awk 'BEGIN{system("echo 111")}'
111
# awk 'BEGIN{system("date")}'
Wed Mar 16 10:59:33 CST 2022

11. 流程控制语句

(1)条件语句:三种格式

if( expression ) action1
[else action2] if ( expression ) { statement1 statement2
}expression ? action1 : action2
# cat awk_if.awk
{if ($1 <= 3)$1 = 5 - $1else$1 = 10 - $1print
}# awk -f awk_if.awk awk_demo
4 Adam 18 Boston
3 Bob 17 Charlotte
2 Chris 17 Dallas
6 David 19 Houston
5 Ellis 20 Illinois

(2)循环语句:三种格式

while (condition) actiondo
action
while (condition)for ( set_counter ; test_counter ; increment_counter ) action
# awk 'BEGIN{ i=10;while(i>=5) {print i;i--} }'
10
9
8
7
6
5

break:跳出循环体

# awk 'BEGIN{ i=10;while(i>=5) {if (i==7) {break} print i;i--} }'
10
9
8

continue:跳出当前循环

# awk 'BEGIN{ i=10;while(i>=5) {i--;if (i==7) {continue} print i} }'
9
8
6
5
4

(3)特殊流程控制:

getline:立刻读取下一行数据(复制给$0,重新设置NF、NR和FNR)

# awk '/A/ {getline;print}{print}' awk_demo
2 Bob 17 Charlotte
2 Bob 17 Charlotte
3 Chris 17 Dallas
4 David 19 Houston
5 Ellis 20 Illinois

next:继续处理下一行内容

# awk '/A/ {next;print}{print}' awk_demo
2 Bob 17 Charlotte
3 Chris 17 Dallas
4 David 19 Houston
5 Ellis 20 Illinois

exit:如果有END,就跳到END,没有就退出脚本。exit后可以跟一个数字,作为退出的值,默认是0,也可以跟一个表达式,表达式的值作为退出的值

# awk '/D/ {exit} {print} END{print 111}' awk_demo
1 Adam 18 Boston
2 Bob 17 Charlotte
111

12. 数组

# 数组定义
array[subscript] = value# 数组遍历
for ( variable in array )action# 判断下标是否存在
subscript in array# 利用内建函数split生成数组
split(): n = split(string, array, separator)#删除数组元素
delete array[subscript]

编辑一个实例脚本:

# cat awk_array.awk
BEGIN{arrayA["a"]="A"arrayA["b"]="B"arrayA["c"]="C"for (key in arrayA)print key,arrayA[key]print "c" in arrayAdelete arrayAprint arrayA["c"]for (i=1;i<=5;i++)for (j=1;j<=5;j++)arrayB[i,j]=0for (key in arrayB)print key,":",arrayB[key]
}{split($0, arrayC, " ")key=1while (key <= NF){print arrayC[key]key++}
}

这个脚本中,在BEGIN中首先定义了一个关联数组,然后遍历它,删除它。之后定义了一个模拟的二维数组,遍历它。最后使用 split 函数将 awk_demo 中的数据拆分成数组,也遍历。

# awk -f awk_array.awk awk_demo
a A
b B
c C
142 : 0
14 : 0
43 : 0
15 : 0
44 : 0
21 : 0
45 : 0
22 : 0
51 : 0
23 : 0
52 : 0
24 : 0
53 : 0
25 : 0
54 : 0
31 : 0
55 : 0
32 : 0
33 : 0
34 : 0
11 : 0
35 : 0
12 : 0
41 : 0
13 : 0
1
Adam
18
Boston
2
Bob
17
Charlotte
3
Chris
17
Dallas
4
David
19
Houston
5
Ellis
20
Illinois

13. 自定义函数

statements可以包含一个return语句

function name (parameter-list)
{ statements }

一个带有参数$1的函数被调用时($1是一个普通变量),函数接收到的参数是变量值的一份拷贝,不是变量本身。 当数组作为函数的参数时,是引用,可以改变原数组的元素。 在函数体内部,参数是局部变量(只在函数执行时存在),它们与程序中其他同名变量没有关联。 如果函数体内某个变量没有出现在参数列表中,它就是全局变量。 如果想设置私有的局部变量,将该变量包含在参数列表的末尾。

# cat awk_func.awk
function max(x,y) {return x > y ? x : y
}
BEGIN{print max(1, max(2, 3))
}# awk -f awk_func.awk
3

shell 编程三剑客之三:awk 详解相关推荐

  1. Linux三剑客之awk详解

    第一篇 awk简介与表达式实例 一种名字怪异的语言 模式扫描和处理,处理数据和生成报告. awk不仅仅是linux系统中的一个命令,而且是一种编程语言:它可以用来处理数据和生成报告(excel):处理 ...

  2. Linux文本三剑客之一——awk详解(1)——awk看这两篇就够啦~PS:文末有练习,来练练手吧

    shell编程三剑客 grep --> egrep --> 文本过滤 查询 awk 文本截取 sed  文本的替换和修改 目录 awk awk也可以做小数运算 awk命令简要处理流程 aw ...

  3. shell编程—tomcat自动发包详解

    tomcat是我们日常使用的java容器,常作为web服务器和Nginx配合使用,运维工程师需要经常发布更新包,而使用shell脚本可以实现高效安全的发包,这以我虚拟机环境来编写. 一. 需求 上传w ...

  4. 文本三剑客之AWK详解

    文本三剑客之AWK awk简介 AWK是一种优良的文本处理工具.它不仅是 Linux中也是任何环境中现有的功能最强大的数据处理引擎之一.这种编程及数据操作语言(其名称得自于它的创始人 Alfred A ...

  5. shell 编程 入门到实战详解

    一. shell变量.循环 概述 Shell是一种具备特殊功能的程序,它提供了用户与内核进行交互操作的一种接口.它接收用户输入的命令,并把它送入内核去执行.内核是Linux系统的心脏,从开机自检就驻留 ...

  6. shell 编程之流程控制语句详解

    前言 可以说,在任何一门编程语言中都离不开流程控制语句,使用流程控制语句可以帮助程序处理各类复杂的操作,常用的流程控制语句,比如: if-else,while,for循环等,在linux 的shell ...

  7. linux shell编程与编辑器使用详解_使用 Mu 编辑器教授 Python | Linux 中国

    导读:Mu 让你轻松学会如何编写 Python 代码. 本文字数:2147,阅读时长大约: 3分钟https://linux.cn/article-12654-1.html 作者:Don Watkin ...

  8. 【 Makefile 编程基础之三】详解 Makefile 变量的定义规则使用!

    本站文章均为 李华明Himi 原创,转载务必在明显处注明: 转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/gcc-makefile/770.html ☞ ...

  9. SHELL编程传递参数方法详解$# $* $0 $1 $2 $...

    目录 实例 特殊字符用来处理参数: $* 与 $@ 区别: 我们可以在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为: $n.n 代表一个数字,1 为执行脚本的第一个参数,2 为执行 ...

最新文章

  1. python字典操作添加_Python字典常见操作实例小结【定义、添加、删除、遍历】
  2. ASP.NET代码对页面输出进行清理
  3. mongo-connector导入数据到Elasticsearch
  4. java exec mvn_maven---常用插件之EXEC
  5. mysql c api 封装_封装MySQL C API 基本操作
  6. linux系统无法识别固态硬盘_linux查看硬盘是不是ssd
  7. mysql left 数学原理,MySQL全面瓦解21(番外):一次深夜优化亿级数据分页的奇妙经历...
  8. 写一篇好的技术文章有多难?
  9. idea错误提示不明显_微信公众号扫一扫功能提示:10003 redirect_uri域名与后台配置不一致错误解决方案...
  10. 程序员如果不能干到退休,那程序员的路何去何从?
  11. 简析运维监控系统及Open-Falcon
  12. scrum 11.27
  13. JSP与Servlet的区别与联系,JSP与JavaScript的区别
  14. 手把手教你自制一寸两寸照
  15. 在Ubuntu8.10中启用TrackPoint
  16. 去掉scan pattern多余port的方法
  17. Yocto系列讲解[理论篇] 45 - bb文件中函数实操演示(3)继承自己的class
  18. vue报错:vue.js:634 [Vue warn]: Cannot find element: #app
  19. Android手机app的adb命令测试电量
  20. 垃圾分类小程序,包含垃圾图片识别,答题,添加垃圾,搜索垃圾,科普视频等功能

热门文章

  1. 人工智能时代,主要带来的变革有哪些?
  2. Oracle基本语法及例子
  3. 学习笔记 | 建站流程
  4. 通信网基础作业答案整理
  5. 般若堂--Spring Boot系列之参数校验
  6. Windows 10免费原生打开HEIC格式的图片(无需转换,直接用照片打开)
  7. UITT不忘初心,为交易而生
  8. 机器视觉系列(五)——镜头部分
  9. blast java_使用python实现BLAST
  10. IEMS_8_图片识别_2