【Linux】awk命令学习

最近用的比较多,学习总结一下。
文档地址:https://www.gnu.org/software/gawk/manual/gawk.html

  • 一、awk介绍
  • 二、语句结构
    • 1.条件控制语句
      • 1)if
      • 2)for
      • 3)while
      • 4)break&continue&next&exit
    • 2.比较运算符
    • 3.逻辑运算符
  • 三、常用内置变量
    • 1.$0 & $n
    • 2.NR & FNR
    • 3.FS & OFS
    • 4.NF
    • 5.ARGIND
  • 四、真实业务场景下的一些demo
    • 1.A&B两文件之间做diff,筛选出B中存在A中不存在的行
    • 2.修改文件中字段间的分隔符
  • 五、相关面试题
    • 1.词频统计
      • PS

一、awk介绍

awk是一个文本编辑器,按照指定的记录分隔符和字段分割符将文件分行分字段,然后一行一行的流式处理数据。

二、语句结构

该命令的一般格式为:awk 'BEGIN{动作} 条件类型1{动作1} 条件类型2{动作2} ... END{动作}' filename

BEGIN中的动作最先执行,一般用来对处理过程的参数如分隔符等进行设置,然后每条数据都依次会根据匹配到的条件类型执行相应的动作,当所有的数据都被处理完成,END中的动作最后执行。BEGIN和END部分都可以省略。

动作中可以是简单的单语句操作,也可以是复杂的多语句操作,像for循环、while循环等,总之,可以像python和java等高级语言那样自由的编写代码。

几个注意的点:

1)整个命令必须用单引号''包裹。如果命令体太长需要分行写,则前一个'必须在第一行,后一个'必须在最后一行,也就是在后一个'的所在行必须结束整个命令。
2)动作语句单语句可以不使用花括号{}包裹,复合语句需要使用花括号{}包裹。
3)条件语句中可以使用小括号()改变计算的优先级。
4)如果命令中需要引号引用字面量,需要使用双引号""
5)每一行记录匹配一个条件类型后,其余的条件类型也会继续匹配,能匹配几个条件类型,就会执行几个对应的动作。
6)如果省略条件类型,也就是对应的动作一定执行,则动作语句必须使用花括号{}包裹,否则报语法错误。
7)动作语句省略时,默认{print $0}

1.条件控制语句

具体参考https://www.gnu.org/software/gawk/manual/gawk.html#Control-Statements-in-Actions,这里只列举部分常用示例。

1)if

语句格式:if (condition) then-body [else else-body]
如果else关键字和then-body语句处在同一行 且 then-body不是复合语句,也就是没有使用花括号{}包裹,那么elsethen-body之间必须使用分号;隔开,否则报语法错误。

[wdy@node1 ~]$ cat test1
A B C
D E F
G H

# 这种情况必须带分号
[wdy@node1 ~]$ cat test1 | awk '{if(NF>=3) print ">=3";else print "<3"}'
>=3
>=3
<3

# then-body被{}包裹可以去分号
[wdy@node1 ~]$ cat test1 | awk '{if(NF>=3){print ">=3"}else print "<3"}'
>=3
>=3
<3

2)for

语句格式:for (initialization; condition; increment) body

[wdy@node1 ~]$ cat test1 | awk '{for(i=1;i<=NF;i++) print $i}'
A
B
C
D
E
F
G
H

3)while

语句格式:while (condition) body

[wdy@node1 ~]$ cat test1 | awk '{i=1;while(i<=NF) {print $i;i++}}'
A
B
C
D
E
F
G
H

4)break&continue&next&exit

这几个都是循环体中的关键字,主要对比下作用上的区别:

  • break:直接结束当前整个循环体。
  • continue:跳过本次循环循环体之后的语句,直接进入下一次循环。
  • next:跳过当前行。这个关键字和continue容易混淆,看起来作用好像差不多,区别在于所结束的“循环对象”不同。awk中数据是一行行读入的,这本身就可以看成是一个循环;而一般我们自己定义的循环,是从列或其他维度循环,因此如果我们定义的循环维度不是从行的维度,那么next是直接结束当前行相关的任何操作跳到下一行。而continue在跳过循环体之后的语句后会继续执行与当前行相关的操作。所以我觉得理解这两个关键字的区别从循环的维度是行还是列可能更好理解一些。
  • exit:如果有END语句,则直接执行END中的动作再结束awk命令,如果没有END语句,则直接结束命令。
# break
[wdy@node1 ~]$ cat test1 | awk '{
> for(i=1;i<=NF;i++){
> if($i=="A") break;print $i}}'
D
E
F
G
H

# continue
[wdy@node1 ~]$ cat test1 | awk '{
> for(i=1;i<=NF;i++){
> if($i=="A") continue;print $i}}'
B
C
D
E
F
G
H

# next
[wdy@node1 ~]$  cat test1 | awk '{
> for(i=1;i<=NF;i++){
> if($i=="B") next;print $i}}'
A
D
E
F
G
H

# exit
[wdy@node1 ~]$ cat test1 | awk '{
> for(i=1;i<=NF;i++){
> if($i=="B") exit;print $i}}
> END{print "finish"}'
A
finish

2.比较运算符

文档:https://www.gnu.org/software/gawk/manual/gawk.html#Comparison-Operators
在这里插入图片描述

3.逻辑运算符

!:not,取反
&&:and
||:or

三、常用内置变量

1.$0 & $n

$0表示当前行这整个记录,$n表示当前行的第n个字段。

[wdy@node1 ~]$ cat test1 
A B C
D E F
G H

[wdy@node1 ~]$ cat test1 | awk '{print $0}'
A B C
D E F
G H

[wdy@node1 ~]$ cat test1 | awk '{print $1}'
A
D
G

2.NR & FNR

Number of Records & File Number of Records,都用来对文件的当前行计数,从1开始。也就是说第一行的数据,NR=1,第二行的数据,NR=2,…,依次类推。区别在于,FNR只在每个文件内累计,跨文件时重新从1开始计数。NR在整个命令执行期间跨文件计数,相当于记录了整个流式处理过程中到当前为止累计处理的行数。

[wdy@node1 ~]$ awk '{print NR,$0}' test1 test1 
1 A B C
2 D E F
3 G H
4 A B C
5 D E F
6 G H

[wdy@node1 ~]$ awk '{print FNR,$0}' test1 test1 
1 A B C
2 D E F
3 G H
1 A B C
2 D E F
3 G H

3.FS & OFS

Field Separator & Output Field Separator,默认值都是空格。这两个变量都是用来指定字段分隔符。FS指定读数据时的字段分隔符,OFS指定输出数据时的字段分隔符。

# 读取输入流时空格分隔,输出流制表符\t分隔
[wdy@node1 ~]$ awk 'BEGIN{FS=" ";OFS="\t"}{print $1,$2}' test1 
A       B
D       E
G       H

这里有几个需要注意的点:
1)指定FS时,如果文件中的字段分隔符为\t,而FS指定的是空格,那么么文件会被正常划分字段赋值给$n这样子的。如果文件中的分割符是空格,而指定的FS是\t,那么每行数据之间不会被分割,也就是一行同时也是一个字段。可以这么来理解:\t其实也是一种特殊的空白字符,一般包含4个空格。实际\tFS空格,则awk会正常按照空格分隔并忽略多余的空格。实际空格FS\t时,在文本中不能找到\t所对应的空格长度,也就是找不到指定的分隔符,所以一整行数据就会被划分为一个字段了。

[wdy@node1 ~]$ awk 'BEGIN{FS=" "}{print $1}' test1 
A
D
G

[wdy@node1 ~]$ awk 'BEGIN{FS="\t"}{print $1}' test1 
A B C
D E F
G H

2)print后面的变量之间只有以,隔开时指定的OFS才会生效,以,分隔时会将默认值空格替换为指定的OFS插在输出变量之间。
3)如果输出的变量是$0即整行数据,想对这整行重新使用指定的OFS分隔输出,直接print $0并不会改变输出记录的原有分隔符。因为就像上面说到的那样,以,分隔时会将默认值空格替换为指定的OFS插在输出变量之间,$0本身只是一个变量而已,如果有这种需求,可以通过下面的这种技巧实现:

# 可以看到输出时并没有将分割符重新转为\t
[wdy@node1 ~]$ awk 'BEGIN{FS=" ";OFS="\t"}{print $0}' test1 
A B C
D E F
G H

# 使用下面这种技巧实现转换
[wdy@node1 ~]$ awk 'BEGIN{FS=" ";OFS="\t"} $1=$1{print $0}' test1 
A       B       C
D       E       F
G       H

可以这么理解下面这种重新赋值的技巧原理:如果不重新赋值,$0只是记录了一个原始数据行的变量而已,即使输出$1、$2等,awk只会根据FS对$0进行分割,输出时变量之间再按照指定的OFS分隔输出。但是这并不会影响原来$0的值。如果使用了$1=$1这种重新赋值的技巧,awk会将所有的$n变量重新计算一遍,所以此时的$0的分隔符就会被指定的OFS替换。

4.NF

Number of Fields,记录了每行数据的字段总数,从1开始计数,值受FS的影响。

[wdy@node1 ~]$ awk 'BEGIN{FS=" "}{print NF,$0}' test1 
3 A B C
3 D E F
2 G H

5.ARGIND

Argument Index,用来标识当前处理的文件序号,从1开始计数。

[wdy@node1 ~]$ awk 'BEGIN{FS=""} {print ARGIND,$0}' test1 test1 
1 A B C
1 D E F
1 G H
2 A B C
2 D E F
2 G H

四、真实业务场景下的一些demo

1.A&B两文件之间做diff,筛选出B中存在A中不存在的行

[wdy@node1 ~]$ cat test1
A B C
D E F
G H

[wdy@node1 ~]$ cat test2
A B C
A1 B1 C1
A2 B2 C2

[wdy@node1 ~]$ awk 'BEGIN{FS=" "} NR==FNR{a[$0];next} !($0 in a)' test1 test2
A1 B1 C1
A2 B2 C2

上面这段命令,会先以第一个文件中每行记录的值作为数据a的索引建立一个数组a,然后在第二个文件中以每一行的值为索引判断是否在数组a当中,索引不在a中该行记录就会被输出。这里有个点需要注意,就是当动作语句省略时,默认是打印输出当前行。

2.修改文件中字段间的分隔符

[wdy@node1 ~]$ cat test1
A B C
D E F
G H

[wdy@node1 ~]$ awk 'BEGIN{FS=" ";OFS="\t"} $1=$1{print $0}' test1
A       B       C
D       E       F
G       H

五、相关面试题

1.词频统计

[wdy@node1 ~]$ cat data 
A B A
C D A
B C A

[wdy@node1 ~]$ cat data | awk '
> BEGIN{OFS="\t"}
> {for(i=1;i<=NF;i++) freq[$i]++}
> END{for(word in freq) print word,freq[word]}' | sort -rnk 2
A       4
C       2
B       2
D       1

主要思路是循环遍历每行中的各个字段,以字段值为索引建立数组freq,如果遇到相同的索引则对应索引位置处的值+1。最后打印这个数组结果就可以了。后面的sort命令是对结果进行下排序输出。
关于sort命令,参数代表的含义:
-r:reverse,逆序输出
-n:numeric-sort,根据字符串对应的数值大小来排序,而非字符串的字典顺序。这里是统计频次。
-k:key,指定用来排序的key,上面用2来指定根据频次排序,2代表的是第2列。

PS

实现awk词频统计过程中发现了个问题,当直接在虚拟机中vim新建data文件时,结果正常:

[wdy@node1 ~]$ cat data | awk '
> BEGIN{OFS="\t"}
> {for(i=1;i<=NF;i++) freq[$i]++}
> END{for(word in freq) print word,freq[word]}' | sort -rnk 2
A       4
C       2
B       2
D       1

当在本地(win10)建好数据文件上传到虚拟机中时,结果错误:

[wdy@node1 ~]$ cat data_win | awk '
> BEGIN{OFS="\t"}
> {for(i=1;i<=NF;i++) freq[$i]++}
> END{for(word in freq) print word,freq[word]}' | sort -rnk 2
A       3
C       2
B       2
D       1
A       1

也就是A本来是4,在后面被拆分成了3+1。

查看一下两文件中的全部内容有什么区别:

[wdy@node1 ~]$ cat -A data
A B A$
C D A$
B C A$

[wdy@node1 ~]$ cat -A data_win 
A B A^M$
C D A^M$
B C A^M$

Unix/Linux系统中,cat -A命令用于显示文件中的控制字符和非打印字符。在cat -A的输出中,$符号通常用来表示行尾,而M通常表示一个回车符(Carriage Return)。

问题就出现在这,win10上传的文件换行符和linux系统的换行符不一致,虽然不影响awk对行的划分,但是对于除了(1, 1)位置的A,data时A不变,而在data_win中就变成了A\r这个整体,验证一下想法是否正确:

[wdy@node1 ~]$ cat data_win | awk '
> BEGIN{OFS="\t"}
> {for(i=1;i<=NF;i++) freq[$i]++}
> END{for(word in freq) print word,freq[word]}' | sort -rnk 2| cat -A
A^M^I3$
C^I2$
B^I2$
D^I1$
A^I1$

可以看到,之所以上面A分两次输出,就是因为其中有的A附带的有隐藏字符。这个问题可以通过dos2unix data_win转换一下文本格式解决:

[wdy@node1 ~]$ dos2unix data_win
[wdy@node1 ~]$ cat -A data_win 
A B A$
C D A$
B C A$

[wdy@node1 ~]$ cat data_win | awk '
> BEGIN{OFS="\t"}
> {for(i=1;i<=NF;i++) freq[$i]++}
> END{for(word in freq) print word,freq[word]}' | sort -rnk 2
A       4
C       2
B       2
D       1

调试过程中还发现一个需要注意的地方,linux中vim编辑器创建的文本,哪怕最后一行没有手动换行,系统也会给最后一行添加换行符,windows中用文本编辑器创建不会。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/591095.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

20240503解决Ubuntu20.04和WIN10双系统下WIN10的时间异常的问题

20240503解决Ubuntu20.04和WIN10双系统下WIN10的时间异常的问题 2024/5/3 9:33 缘起&#xff1a;因为工作需要&#xff0c;编译服务器上都会安装Ubuntu20.04。 但是因为WINDOWS强悍的生态系统&#xff0c;偶尔还是有必须要用WINDOWS的时候&#xff0c;于是也安装了WIN10。 双系…

软件应用开发安全设计指南

1.1 应用系统架构安全设计要求 设计时要充分考虑到系统架构的稳固性、可维护性和可扩展性&#xff0c;以确保系统在面对各种安全威胁时能够稳定运行。 在设计系统架构时&#xff0c;要充分考虑各种安全威胁&#xff0c;如DDoS攻击、SQL注入、跨站脚本攻击&#xff08;XSS&…

2022 亚马逊云科技中国峰会,对话开发者论坛

目录 前言 最近整理资料发现还有一些前 2 年的内容没发出来&#xff0c;故补发记录&#xff0c;每年都有新的感悟。 开发者论坛 1. 你认为什么是开发者社区&#xff0c;如何定义一个成功的开发者社区&#xff1f; 我认为可以把开发者社区看成一个 “产品” 来对待&#xff…

SpringBoot @DS注解 和 DynamicDataSource自定义实现多数据源的2种实现方式

前言 在实际的项目中&#xff0c;我们经常会遇到需要操作多个数据源的情况&#xff0c;SpringBoot为我们提供了多种实现多数据源的方式。本文将介绍两种常见的方式&#xff1a;使用DS注解实现多数据源的切换以及使用DynamicDataSource自定义实现多数据源的切换。 我们将分别介…

【Unity】在空物体上实现 IPointerClickHandler 不起作用

感谢Unity接口IPointerClickHandler使用说明_哔哩哔哩_bilibiliUnity接口IPointerClickHandler使用说明, 视频播放量 197、弹幕量 0、点赞数 3、投硬币枚数 2、收藏人数 2、转发人数 0, 视频作者 游戏创作大陆, 作者简介 &#xff0c;相关视频&#xff1a;在Unity多场景同时编辑…

扩散模型(Diffusion Model)概述

扩散模型&#xff08;Diffusion Model&#xff09;是图像生成模型的一种。有别于此前 AI 领域大名鼎鼎的 GAN、VAE 等算法&#xff0c;扩散模型另辟蹊径&#xff0c;其主要思想是一种先对图像增加噪声&#xff0c;再逐步去噪的过程&#xff0c;其中如何去噪还原图像是算法的核心…

移动机器人系统与技术:自动驾驶、移动机器人、旋翼无人机

这本书全面介绍了机器人车辆的技术。它介绍了道路上自动驾驶汽车所需的概念。此外&#xff0c;读者可以在六足机器人的构造、编程和控制方面获得宝贵的知识。 这本书还介绍了几种不同类型旋翼无人机的控制器和空气动力学。它包括各种旋翼推进飞行器在不同空气动力学环境下的模…

备考2024年小学生古诗文大会:吃透10道历年真题和知识点(持续)

根据往年的安排&#xff0c;2024年上海市小学生古诗文大会预计还有一个月就将启动。我们继续来随机看10道往年的上海小学生古诗文大会真题&#xff0c;这些题目来自我去重、合并后的1700在线题库&#xff0c;每道题我都提供了参考答案和独家解析。 根据往期的经验&#xff0c;只…

pg数据库学习知识要点分析-1

知识要点1 对象标识OID 在PostgreSQL内部&#xff0c;所有的数据库对象都通过相应的对象标识符&#xff08;object identifier&#xff0c;oid&#xff09;进行管理&#xff0c;这些标识符是无符号的4字节整型。数据库对象与相应oid 之间的关系存储在对应的系统目录中&#xf…

如何使用 Node.js 开发一个文件上传功能?

在 Node.js 中实现文件上传功能可以通过多种方式完成&#xff0c;但其中最常用的方法之一是使用 Express 框架和 Multer 中间件。Express 是一个流行的 Node.js Web 框架&#xff0c;而 Multer 是一个用于处理文件上传的中间件。 步骤 1: 准备工作 首先&#xff0c;确保你已经…

基于Springboot的旅游管理系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的旅游管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&…

数字旅游以科技创新为核心竞争力:推动旅游服务的智能化、高效化,满足游客日益增长的旅游需求

一、引言 随着科技的飞速发展&#xff0c;数字旅游作为旅游业与信息技术结合的产物&#xff0c;正以其独特的魅力改变着传统旅游业的格局。科技创新作为数字旅游的核心竞争力&#xff0c;不仅推动了旅游服务的智能化、高效化&#xff0c;更满足了游客日益增长的旅游需求。本文…

HIVE数据导出

HIVE数据导出 1.INSERT OVERWRITE LOCAL DIRECTORY "路径" SELECT 查询语句; INSERT OVERWRITE LOCAL DIRECTORY "/usr/local/soft/hive-3.1.2/data/output" select * from learn2.partition_student6; 导出数据时 通过执行MapReduce任务导出到本地文件系统…

Python安装win32api

&#x1f4da;博客主页&#xff1a;knighthood2001 ✨公众号&#xff1a;认知up吧 &#xff08;目前正在带领大家一起提升认知&#xff0c;感兴趣可以来围观一下&#xff09; &#x1f383;知识星球&#xff1a;【认知up吧|成长|副业】介绍 ❤️感谢大家点赞&#x1f44d;&…

【云原生】Docker 实践(二):什么是 Docker 的镜像

【Docker 实践】系列共包含以下几篇文章&#xff1a; Docker 实践&#xff08;一&#xff09;&#xff1a;在 Docker 中部署第一个应用Docker 实践&#xff08;二&#xff09;&#xff1a;什么是 Docker 的镜像Docker 实践&#xff08;三&#xff09;&#xff1a;使用 Dockerf…

【neteq】tgcall的调用

G:\CDN\P2P-DEV\Libraries\tg_owt\src\call\call.cc基本是按照原生webrtc的来的:G:\CDN\P2P-DEV\tdesktop-offical\Telegram\ThirdParty\tgcalls\tgcalls\group\GroupInstanceCustomImpl.cpptg对neteq的使用 worker 线程创建call Call的config需要neteqfactory Call::CreateAu…

Git可视化工具tortoisegit 的下载与使用

一、tortoisegit 介绍 TortoiseGit 是一个非常实用的版本控制工具&#xff0c;主要用于与 Git 版本控制系统配合使用。 它的主要特点包括&#xff1a; 图形化界面&#xff1a;提供了直观、方便的操作界面&#xff0c;让用户更易于理解和管理版本控制。与 Windows 资源管理器…

MATLAB中自定义栅格数据地理坐标R,利用geotifwrite写入tif

场景描述&#xff1a; 有时候将nc格式的数据转成tiff&#xff0c;或者是将一个矩阵输出成带有地理坐标信息tiff数据时&#xff0c;常常涉及到空间参考的定义和geotiffwrite()函数。 问题描述&#xff1a; 以全球数据为例&#xff0c;今天发现在matlab中对矩阵进行显示后&…

android zygote进程启动流程

一&#xff0c;启动入口 app_main.cpp int main(int argc, char* const argv[]) {if (!LOG_NDEBUG) {String8 argv_String;for (int i 0; i < argc; i) {argv_String.append("\"");argv_String.append(argv[i]);argv_String.append("\" ")…

DiffSeg——基于Stable Diffusion的无监督零样本图像分割

概述 基于计算机视觉的模型的核心挑战之一是生成高质量的分割掩模。大规模监督训练的最新进展已经实现了跨各种图像风格的零样本分割。此外&#xff0c;无监督训练简化了分割&#xff0c;无需大量注释。尽管取得了这些进展&#xff0c;构建一个能够在没有注释的零样本设置中分…