玩转linux三剑客-这应该是我看到过最全的awk学习总结了


在这里插入图片描述

前言

awk 可以说是 linux 运维人员必备的一门语言命令了,而且开发和测试人员也应该掌握它,awk 善于对文本进行处理,理论上可以替代 grep 命令

我为什么要把 awk 单独拿出来做一篇博文呢?因为 awk 功能确实太强大了,grep 和 sed 相对而言要好掌握不少,awk 其本身就算是一门编程语言,awk 脚本的写法可以说是非常灵活的,理论上可以替代 grep,所以我觉得很有必要将 awk 专门拎出来做一篇学习总结 awk 脚本的博文!

一些心得

有的人会认为初学 awk 的时候命令太多,太难记忆了,而且语法规则有些难以理解,其实不然,我们可以用这样的视角去看 awk 命令,我们可以把 awk 这个命令执行脚本的过程看成一个大的 for 循环,且是按行来循环,action 中我们去写对当前行中每一列进行的操作,这样看 awk 就会发现简单了许多,另外 awk 多了一个模式匹配的东西,这个模式匹配我们就直接看成遍历到当前行,里头写的一个条件语句即可,再另外 BEGIN 语句和 END 语句分别看成在 for 循环开头和 for 循环结束后的操作即可。很简单对吧!

历史溯源

awk 为什么叫做 awk 呢?因为其命名源于三位大佬的名字首字母:Alfred Aho,Peter Weinberger, 和 Brian Kernighan 的 Family Name 的首字符,这几位是 awk 工具的创始人

awk 基本格式

# 直接依进行 awk 命令操作
awk [option] 'pattern{action}' var=value file(s)

# 可以依据文件中的 awk 命令脚本进行操作,-f 指定 awk 脚本的文件
awk [option] -f scriptfile var=value file(s)

awk 执行顺序

我们先看一个例子

# 写法一
awk '{FS=":"} {pirnt $1}'

# 写法二
awk 'BEGIN{FS=":"} {pirnt $1}'

# 写法三
awk -F ':' '{print $1}'

写法一我们认为它是按照:做分割输出第一列,实际上并不是,写法二才是正确的。那为什么会导致这种情况的产生呢?因为在没有指定 BEGIN 变量的时候,awk 命令会先去读取第一行数据,它不会管你的 FS 设置为多少,awk 命令的底层操作顺序就是先直接去读取第一行数据,使用默认空格分隔符,然后发现命令中有 FS 分割,但已经读取了第一行改不了分割的规则了,所以第一行直接打印,然后从第二行开始分割都是正常的了。

所以我们明白一个道理就是像 FS 这类改变 awk 行数据排列规则的命令应该在前面带上 BEGIN,让其在读取第一行之前就应该被处理到!

用写法三也是正确的

awk 的大致流程就是逐行处理,把行作为赋值给 $0,然后将 $0 按照默认空格符做切分,切分为 $1, $2, $3...,然后执行编程动作,最后输出到屏幕

awk 如何分割语句

awk 基本分隔样式

# awk 选项 '模式语句{动作语句}'
awk [option] 'pattern{action}'

awk 自成一门编程语言,语句的分隔是有特点的。涉及到''{},这些符号,我们总结出特点有以下三点:

  • 1.被 {}包裹的是 awk 的 action 动作,也就是说下面 awk 的各种语法放在{}才能被 awk 语法编译执行,{}中被识别为 awk 的语法
  • 2.{}中,awk 的脚本语句之间需要用;隔开,这一点与其他编程语言类似
  • 3.{}外,单引号内的空间是模式语句,模式可以直接做真假值的判定,不用加 if 语句,比如NR==1,awk 会逐行进行的判定,当 awk 遍历到该行的时候,若此条件判定为真,就会输出该行相应的数据,否则不输出

基本形式如下:

# 组合语句
{statements;...}

# 基本写法
awk '{if(condition1) {statements1;...} else if(condition2) {statements2;...} else{statements;...}}' test.log

# 基本写法,这里 statements 可以代表 if else 的表达式
awk '{statements1; statements2; if(condition) {statements3;...}}' test.log

# 基本形式,{} 外面可以直接写条件
awk 'condition {statments;}'

awk 常用选项参数关键字

选项

# 指定分隔符
-F
awk -F ':' '{print $1}' test.log
# 指定 awk 的脚本文件
-f
awk -f awkfile.sh test.log
# 设置变量
-v
awk -v 'a=1' '{print a}' test.log
# 忽略大小写
IGNORECASE=1
# 行号
NR
# 列数
NF
# 记录分隔符,也就是 awk 默认是以回车符作为一行行读取的证据,记录分隔符是做这个的
RS
# 被识别的字段分隔符
FS
# 输出的字段分隔符
OFS

关键字

# awk 开始与结束执行
BEGIN
END
# exit 后可以接上参数作为后序 awk 状态信息的输入。跳出 awk
exit
# 在当前这个关键字处立即停止并进入下一行
next

awk 最基础操作

# 打印第一列
awk '{print $1}' test.log

# 格式化打印第一列第二列
awk '{print $1,\t,$2}' test.log

# 打印第一行
awk 'NR==1 {print $0}' test.log

# 打印匹配到数字的行的行号和第一列
awk '/[0-9]/ {print NF,$1}' test.log

# 打印第一列匹配到数字的行
awk '$1~/[0-9]/ {print $0}' test.log

# 不匹配数字的行
awk '!/[0-9]/ {print $0}' test.log

# 设置 : 为分隔符
awk -F ':' '{print $1}' test.log
awk 'BEGIN{FS==":"} {print $1}' test.log

# 多重分割,用空格和逗号做分给,逻辑上是先用空格分隔,之后空格里在用逗号分隔
awk -F '[ ,]' '{print $1,$2}' test.log

# 不区分大小写匹配
awk 'BEGIN{IGNORECASE=1} /aaa/' test.log

# 输出 0-1 的随机数
echo | awk '{srand(); print rand()}'

awk 变量

对于没有声明的变量,awk 会直接赋予其初值 0,字符串则赋予初值空字串,这是一个特点需要记住

数字,字符串 awk 有常见的数字类型,字符串类型,还有内置变量比如 NR 这些

数组 awk 的数组也蛮有意思,awk 支持关联数组这种数据结构,形式上和普通数组写法一致,但是效果上更像是 java 中的 map 这种结构,因为 awk 中的关联数组的下标可以是字串

# 声明
a["aaa"]+=$1;
b[$2]=1;
# 删除整个数组
delete a;
# 删除某个数组元素
delete b[$2];

多维数组 至于多维数组 awk 本身是不支持的,所以我们只能用一维数组去模拟多维数组

awk 运算符

# 比较运算符
< <= > >= == !=
# 支持赋值运算符
= += -= *= /= %= ^=
# 三元运算符
?:
# 常规运算符
+ - * / %
# 逻辑运算符
&& || ! 
# 加加与减减
++ --
# 匹配正则表达式和不匹配正则表达式
~ !~
# 变量引用
$
# 数组成员
in

awk 条件语句

awk 支持 if 语句。基本形式如下:

# if 语句
if(condition) {statements;...}
# if else 语句
if(condition) {statements1;...} else{statements2;...}
# if elseif else 语句
if(condition1) {statements1;...} else if(condition2) {statements2;...} else {statements3;...}

awk 循环语句

awk 支持 for 和 while 循环语句。基本形式如下:

# for 语句
for(expr1;expr2;expr3) {statements;...}
# for in 语句
for(arg in arr) {statements;...}
# while 语句
while(condition) {statements;...}
# do while 语句
do{statements;...} while(condition)

# 跳出与继续 for while 循环
break
continue

awk 输入输出语句

输入语句

# 通过 -f 指定 awk 脚本文件来源
awk -f [FILE] test.log

输出语句

# print 直接打印
awk '{print $0}' test.log
# printf 格式化打印
awk '{printf "%s\t%s\n",$1,$2}' test.log

# 浮点
%f
# 单个字符
%c
# 字符串
%s
# 有符号整型
%d
# 自动选择合适表示法
%g

awk 常用函数

内置函数

# 求开方
sqrt(arg)
# 截断取整
int(arg)
# 函数作用是把 rand() 函数种子置为 srand() 的参数,若没有参数,则参数是某天一个时间。返回的先前的随机种子,默认初始的随机种子是 1。简要概括就是 srand(arg) 是设置随机种子返回之前的随机种子
srand()
srand(seed)
# 随机数产生,rand() 生成 0-1 随机数,不设置 srand() 则随机种子是 1,rand() 起不到随机效果,输出 rand() 是固定值
srand(arg) 后再执行 rand()

# 字符串替换,将原字符串(第三个参数)中的某个正则匹配到的字符串(第一个参数)替换成某个字符串(第二个参数)
sub(regx,segmentStr,originStr)
# 字符串截取
substr(str,num1,num2)
# 返回第一次匹配到的下标
index(segmentStr,originStr)
# 长度方法
length(arg)
# 分割成数组
split(str,arr,分割符)
# 转化为小写
tolower(str)
# 转化为大写
toupper(str)

# 返回 1970/1/1 到目前的整秒数
systime()

自定义函数

举个小例子

awk '{\
        function addition(num1, num2){\
            result=num1+num2;\
            return result;\
        }\
        BEGIN{\
            res=addition(10,20);\
            print "10 + 20 = " res;\
        }\
}' test.log

awk 注意小细节

  • 我在实际中测试发现,我们通过 -F 指定分隔符的时候,如果分隔符是空格,则会默认按照贪婪模式去匹配,即去查找最长的空格作为分隔符,若我们指定其他符号作为分隔符,比如说英文字母,则它会以非贪婪模式去匹配,即遇到一个就作为分隔符
  • 前面我们讲解了如果把 awk 的模式匹配看成 awk 循环每一行中的条件语句,把 BEGIN 和 END 看成 awk 遍历整个文本之前和之后的内容,awk 的模式匹配语句是用来做真假值的判断的,如果读到该行模式匹配是真就输出,假就不输出,但是要注意的是这种写法是错误不允许的就是模式匹配后面加上了 END{},模式匹配后要么是空的,要么是 {},不能跟上 END{},下面举出正确例子
    # 正确写法!!!!
    awk 'NR==1'
    awk 'BEGIN{...} NR==1'
    awk 'BEGIN{...} NR==1 {...}'
    awk 'BEGIN{...}'
    awk 'END{...}'
    
    这下面是错误写法:
    awk 'NR==1 END{...}' 这是有问题的!!!
    
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 终极编程指南 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读