【Linux】awk命令学习

news2024/11/18 10:21:51

最近用的比较多,学习总结一下。
文档地址: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中用文本编辑器创建不会。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

线性数据结构-手写链表-LinkList

为什么需要手写实现数据结构&#xff1f; 其实技术的本身就是基础的积累和搭建的过程&#xff0c;基础扎实 地基平稳 万丈高楼才会久战不衰&#xff0c;做技术能一通百&#xff0c;百通千就不怕有再难得技术了。 一&#xff1a;链表的分类 主要有单向&#xff0c;双向和循环链表…

陈随易:论技术思维和产品思维

大家好&#xff0c;我是不被定义的前端之虎陈随易。 我的个人网站是&#xff1a;https://chensuiyi.me&#xff0c;欢迎大家眼熟我。 写这篇文章呢&#xff0c;源于一次群聊。 群友有一个产品&#xff0c;其中涉及到免费用户和付费用户对 pdf 的查看权限问题&#xff0c;使用…

EPAI手绘建模APP颜色、贴图、材质、样式

⑦ 颜色选择页面 1) 颜色环选色。 图 65 颜色选择器-颜色环 2) RGB选色。 图 66 颜色选择器-RGB 3) HSL选色。 图 67 颜色选择器-HSL 4) 国风颜色库选色。 图 68 颜色选择器-国风 5) CSS颜色库选色。 图 69 颜色选择器-CSS 6) 历史颜色&#xff1a;保存最近使用的多个颜色&…

鸿蒙开发仿咸鱼TabBar

鸿蒙开发自定义TabBar&#xff0c;实现tabBar 上中间按钮凸起效果 第一步、定义数据模型 export default class TabItemData{defaultIcon: ResourceselectedIcon: Resourcetitle: stringisMiddle: booleanconstructor(defaultIcon:Resource, selectedIcon:Resource, title:st…

基于改进暗原色先验和颜色校正的水下图像增强,Matlab实现

博主简介&#xff1a; 专注、专一于Matlab图像处理学习、交流&#xff0c;matlab图像代码代做/项目合作可以联系&#xff08;QQ:3249726188&#xff09; 个人主页&#xff1a;Matlab_ImagePro-CSDN博客 原则&#xff1a;代码均由本人编写完成&#xff0c;非中介&#xff0c;提供…

初始化Linux或者Mac下Docker运行环境

文章目录 1 Mac下安装Docker2 Linux下安装Docker2.1 确定Linux版本2.2 安装Docker2.3 配置加速镜像 3 Docker安装校验4 安装docker-compose4.1 直接下载二进制文件4.2 移动二进制文件到系统路径4.3 设置可执行权限4.4 验证安装 1 Mac下安装Docker mac 安装 docker 还是比较方便…

OpenCV如何为我们的应用程序添加跟踪栏(71)

返回:OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇:OpenCV的周期性噪声去除滤波器(70) 下一篇 :OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 在前面的教程中&#xff08;关于使用 OpenCV 添加&#xff08;混合&#xff09;两个图像和…

基于SSM的“软件缺陷管理系统”的设计与实现(源码+数据库+文档+PPT)

基于SSM的“软件缺陷管理系统”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SSM 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统功能结构图 管理员登录 首页 项目经理列表 项目经理添加…

c#学习基础2

四、复杂数据类型--结构体 1.基本概念 、2.基本语法 结构体一般写在namespace语句块中&#xff1b; 结构体关键字 struct 3.实例 4.结构体的使用 5.访问修饰符 6.结构体的构造函数 7.总结 五、排序初探 1&#xff09;冒泡排序 1.排序的基本概念 2.冒泡排序的基本原理 两…

Jenkins docker部署springboot项目

1、创建jenkins容器 1&#xff0c;首先&#xff0c;我们需要创建一个 Jenkins 数据卷&#xff0c;用于存储 Jenkins 的配置信息。可以通过以下命令创建一个数据卷&#xff1a; docker volume create jenkins_data启动 Jenkins 容器并挂载数据卷&#xff1a; docker run -dit…

mac查看Linux服务器的性能

mac上安装 linux系统 如果有 linux服务器账号密码&#xff0c;那么上一部可忽略&#xff1b; 比如&#xff1a;直接连接阿里云或腾讯云账号 1. 安装termius 链接: https://pan.baidu.com/s/1iYsZPZThPizxqtkLPT89-Q?pwdbw6j 提取码: bw6j 官网 Termius - SSH platform for …

YOLO系列自研改进:基于注意力机制的多尺度特征提取模块

目录 一、原理 二、代码 三、在YOLO中的应用 一、原理 这个模块的原理仍然是利用不同大小的卷积核来提取不同尺度的特征,同样将通道划分为两部分,一部分通过注意力机制进行通道信息和空间信息的提取,另一部分通过多个不同大小的卷积核来提取多尺度的特征信息。 二、代码…

开源模型应用落地-LangChain高阶-Tools工具-集成agents(四)

一、前言 LangChain 的 tools 是一系列关键组件&#xff0c;它们提供了与外部世界进行交互的能力。通过适当的使用这些组件&#xff0c;可以简单实现如执行网络搜索以获取最新信息、调用特定的 API 来获取数据或执行特定的操作、与数据库进行交互以获取存储的信息等需求。 本章…

【阿里云服务器】ubuntu 22.04.1安装docker以及部署java环境

我的服务器配置是2GB CPU 2GB 内存 Ubuntu22.04 目录 一、阿里云 ubuntu 22.04.1安装docker 二、docker基础命令 三、Windows电脑访问云服务器 四、安装java环境 安装OpenJDK 8&#xff08;可以根据需要安装其他版本的JDK&#xff09; 安装java的依赖管理工具maven 一、…

机器学习:基于K-近邻(KNN)、高斯贝叶斯(GaussianNB)、SVC、随机森林(RF)、梯度提升树(GBDT)对葡萄酒质量进行预测

前言 系列专栏&#xff1a;机器学习&#xff1a;高级应用与实践【项目实战100】【2024】✨︎ 在本专栏中不仅包含一些适合初学者的最新机器学习项目&#xff0c;每个项目都处理一组不同的问题&#xff0c;包括监督和无监督学习、分类、回归和聚类&#xff0c;而且涉及创建深度学…

《MySQL45讲》读书笔记

重建表 alter table t engine InnoDB&#xff08;也就是recreate&#xff09;&#xff0c;而optimize table t 等于recreateanalyze&#xff0c;让表大小变小 重建表的执行流程 建立一个临时文件&#xff0c;扫描表 t 主键的所有数据页&#xff1b;用数据页中表 t 的记录生…

HTML_CSS学习:CSS盒子模型

一、CSS中常用的长度单位 相关代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>CSS中常用的长度单位</title><style>html{font-size: 40px;}#d1{/*第一种长度单位&…

MySQL CRUD进阶

前言&#x1f440;~ 上一章我们介绍了CRUD的一些基础操作&#xff0c;关于如何在表里进行增加记录、查询记录、修改记录以及删除记录的一些基础操作&#xff0c;今天我们学习CRUD&#xff08;增删改查&#xff09;进阶操作 如果各位对文章的内容感兴趣的话&#xff0c;请点点小…

Python设计模式 - 单例模式

定义 单例模式是一种创建型设计模式&#xff0c; 其主要目的是确保一个类只有一个实例&#xff0c; 并提供一个全局访问点来访问该实例。 结构 应用场景 资源管理&#xff1a;当需要共享某个资源时&#xff0c;例如数据库连接、线程池、日志对象等&#xff0c;可以使用单例模…

OpenCV(六) —— Android 下的人脸识别

本篇我们来介绍在 Android 下如何实现人脸识别。 上一篇我们介绍了如何在 Windows 下通过 OpenCV 实现人脸识别&#xff0c;实际上&#xff0c;在 Android 下的实现的核心原理是非常相似的&#xff0c;因为 OpenCV 部分的代码改动不大&#xff0c;绝大部分代码可以直接移植到 …