awk (转)
Awk是一种处理结构数据并输出格式化结果的编程语言, Awk 是其作者 "Aho,Weinberger,Kernighan" 的简称。
Awk通常被用来进行格式扫描和处理。通过扫描一个或多个文件中的行,查看是否匹配指定的正则表达式,并执行相关的操作。
Awk的主要特性包含:
1. Awk以记录和字段的方式来查看文本文件
2. 和其他编程语言一样,Awk 包含变量、条件和循环
3. Awk能够进行运算和字符串操作
4. Awk能够生成格式化的报表数据
Awk从一个文件或者标准输入中读取数据,并输出结果到标准输出中。
1. Awk的语法
在上诉语法中:
1. search pattern是正则表达式
2. Actions 输出的语法
3. 在Awk 中可以存在多个正则表达式和多个输出定义
4. file 输入文件名
5. 单引号的作用是包裹起来防止shell 截断
2. Awk的工作方式:
1) Awk 一次读取文件中的一行
2)对于一行,按照给定的正则表达式的顺序进行匹配,如果匹配则执行对应的 Action
3)如果没有匹配上则不执行任何动作
4)在上诉的语法中, Search Pattern 和 Action 是可选的,但是必须提供其中一个
5)如果 Search Pattern 未提供,则对所有的输入行执行 Action 操作
6)如果 Action 未提供,则默认打印出该行的数据
7) {} 这种 Action 不做任何事情,和未提供的 Action 的工作方式不一样
8) Action 中的语句应该使用分号分隔
创建一个包含下面内容的文本文件employee.txt 。后续的例子中将会用到该文件
- $cat employee.txt
- 100 Thomas Manager Sales $5,000
- 200 Jason Developer Technology $5,500
- 300 Sanjay Sysadmin Technology $7,000
- 400 Nisha Manager Marketing $9,500
- 500 Randy DBA Technology $6,000
3. Awk 的默认行为
默认的时候awk 打印文件中的每一行
- $ awk '{print;}' employee.txt
- 100 Thomas Manager Sales $5,000
- 200 Jason Developer Technology $5,500
- 300 Sanjay Sysadmin Technology $7,000
- 400 Nisha Manager Marketing $9,500
- 500 Randy DBA Technology $6,000
在上面的例子中,匹配的正则表达式未给出,因此后续的Action 适用所有的行, Action 中的 print 没有任何参数的情况下将打印整行,注意其中的 Action 必须使用 {} 括起来。
4. Awk打印匹配的行
- $ awk '/Thomas/
- > /Nisha/' employee.txt
- 100 Thomas Manager Sales $5,000
- 400 Nisha Manager Marketing $9,500
在上面的例子中,将打印包含Thomas 和 Nisha 的行,上面的列子包含两个正则表达式。 Awk 可以接受任意数量的正则表达式,但是每个组合 ( 正则表达式和对应的 Action) 必须用新行来分隔。
5. Awk仅打印指定的域
Awk包含许多内建的变量,对于每行的记录, Awk 默认按照空格进行分割,并将分隔后的值存入对应的 $n 变量中。如果一行还有 4 个单词,将被分别存储进 $1 $2 $3 $4 中,其中 $0 代表整行。 NF 也是一个内建的变量,代表该行中分割后的变量数。
在上诉例子中$2 和 $5 分别代表名字和薪水,也可以使用 $NF 获得薪水,其中 $NF 代表最后一个字段,在打印语句中逗号是一个连接符号。
6. Awk开始和最后的动作
Awk包含两个重要的关键字 BEGIN 和 END
- Syntax:
- BEGIN { Actions}
- {ACTION} # Action for everyline in a file
- END { Actions }
- # Awk中的注释
在BEGIN 节中的 Actions 会在读取文件中的行之前被执行。
而END 节中的 Actions 会在读取并处理文件中的所有行后被执行。
- $ awk 'BEGIN {print "Name/tDesignation/tDepartment/tSalary";}
- > {print $2,"/t",$3,"/t",$4,"/t",$NF;}
- > END{print "Report Generated/n--------------";
- > }' employee.txt
- Name Designation Department Salary
- Thomas Manager Sales $5,000
- Jason Developer Technology $5,500
- Sanjay Sysadmin Technology $7,000
- Nisha Manager Marketing $9,500
- Randy DBA Technology $6,000
- Report Generated
- --------------
上述的例子为输出结果增加头和尾描述
7. Awk找出员工 ID 大于 200 的员工
- $ awk '$1 >200' employee.txt
- 300 Sanjay Sysadmin Technology $7,000
- 400 Nisha Manager Marketing $9,500
- 500 Randy DBA Technology $6,000
在上述例子中,$1 代表员工 ID ,如果员工 ID 大于 200 则执行默认的打印整行的 Action 。
8. Awk打印技术部员工
$4代表员工所在的部门,如果等于 Technology 则打印出整行
- $ awk '$4 ~/Technology/' employee.txt
- 200 Jason Developer Technology $5,500
- 300 Sanjay Sysadmin Technology $7,000
- 500 Randy DBA Technology $6,000
~操作符是和正则表达式中的值进行比较,如果匹配则打印整行
9. Awk打印技术部门的员工数
在下面的例子中,检查员工的部门是否是Technology ,如果是则递增 count 变量的值。 Count 变量的值在BEGIN 的 Actions 中被初始化为 0 。
- $ awk 'BEGIN { count=0;}
- $4 ~ /Technology/ { count++; }
- END { print "Number of employees in Technology Dept =",count;}' employee.txt
- Number of employees in Tehcnology Dept = 3
在处理的最后(END 的 Actions) ,仅仅打印出 Technology 部门的人数
在Linux awk语言中,提供了很多有意义的函数。例如:
gsub(r,s) 在整个$0中用s代替r
gsub(r,s,t) 在整个t上用s代替r
length(s) 返回s的长度
index(s,t) 返回s中字符串t的第一位置
match(s,r) 测试s是否包含r 子串
spilt(s,a,fs) 用fs(某种分割符号)去拆分s串,将拆分后的数组赋给a
sub(r,s) 用$0中最左边最长的子串去代替s
substr(s,p) 返回字符串s从p开始的后缀部分
substr(s,p,n) 返回字符串s从p开始的长度为n的串
现在分别举例子来看这些具体的应用:
1:gsub
下面一个例子将匹配chenwu串的行替换成yangliu(注意这里要加引号,不然awk会把它当作变量)
- [chenwu@localhost unit9-awkIntroduce]$ cat -n grade.txt
- 1 chenwu 05/99 4811 27
- 2 mary 02/22 1231 30
- 3 tom 09/15 1182 25
- [chenwu@localhost unit9-awkIntroduce]$ awk 'gsub(/chenwu/,"yangliu") {print $0}' grade.txt
- yangliu 05/99 4811 27
2:index(获取ny串在Bonny的起始位置,注意这里的计数与一般的编程语言不同,这里从1开始计数)
- [chenwu@localhost unit9-awkIntroduce]$ awk 'BEGIN {print index("Bonny","ny")}' grade.txt
- 4
3:length
- [chenwu@localhost unit9-awkIntroduce]$ awk '{if($1=="chenwu") print $1,length($1)}' grade.txt
- chenwu 6
4:match
match用来匹配子串(也可以是一个正则表达式 )在父串的位置,如果匹配成功,则返回匹配的位置,否则返回0,例如:
- [chenwu@localhost unit9-awkIntroduce]$ awk 'BEGIN {print match("ABCD",/C/)}'
- 3
- [chenwu@localhost unit9-awkIntroduce]$ awk 'BEGIN {print match("ABCD",/U/)}'
- 0
5:split
- [chenwu@localhost unit9-awkIntroduce]$ awk 'BEGIN {print split("123#45#22",myarray,"#")}'
- 3
6:substr(s,p[,n]) 返回s串中从p开始的后缀部分,n是一个可选参数,表示取多大的长度
例如
- [chenwu@localhost unit9-awkIntroduce]$ awk 'BEGIN {base="I love money"} {print substr(base,3,6)}' grade.txt
- love m
- love m
- love m
awk的这些函数在和shell的管道符结合在一起的时候,可以发挥很大的作用。例如:
返回一个目录下所有文件的句点的前缀名称。(不好使)
- [chenwu@localhost unit9-awkIntroduce]$ ls -l | awk 'BEGIN {if($1!~/^d/) n=index($8,".")}
- {if($1!~/^d/) print n,substr($8,n)}'
- 0
- 0 grade.report
- 0 grade.txt
- [chenwu@localhost unit9-awkIntroduce]$ ls -l
- 总计 12
- -rw-rw-r-- 1 chenwu chenwu 53 06-10 10:16 grade.report
- -rw-rw-r-- 1 chenwu chenwu 64 06-11 10:12 grade.txt
- drwxrwxr-x 2 chenwu chenwu 4096 06-11 10:28 testDir
这里为什么不好使尼?理由是n在begin里定义了后,之后就不会再变了,稍微改造后,就好使了:
- [chenwu@localhost unit9-awkIntroduce]$ ls -l | awk '{n=index($8,".")} {if($1!~/^d/) print substr($8,1,n-1)}'
- grade
- grade
但是这里还是有点瑕疵,你会发现并没有过滤普通目录。grade的上方仍然有输出(空白)
一种较合理的解释是普通目录的确过滤掉了,但是ls -l最上方的总计12 没有过滤到,导致也对它取了次从1到句点.结束的子串,从而打印了空白。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· [AI/GPT/综述] AI Agent的设计模式综述