您当前的位置: 首页 > 

易懂的语言讲解 awk、sed,快速掌握

蔚1 发布时间:2020-04-21 23:30:33 ,浏览量:4

sed、awk 是工作中常用的命令,可显著提高生产效率,也是面试的常客。今天我们就以一篇长文,讲解这两个命令的常见使用,让你的工作如虎添翼。

sed 和 awk 是 Linux 上常用的命令,可以显著增加工作效率。但由于一些文章描述的很是晦涩,让人望而生畏,很多同学错过了这些工具,很是可惜。我这里总结了一些常用的操作,希望能帮你快速掌握这两个命令。

1、AWK

敢用自己的名字做软件名字的,都有非常强大的自信。比如,垠语言什么的。

awk的命名得自于它的三个创始人姓别的首字母,都是80 来岁的老爷爷了。当然也有四个人的组合:流行的 GoF 设计模式。但对于我这游戏爱好者来说,想到的竟然是三位一体,果然是不争气啊。

它长的很像 C,为什么这么有名,除了它强大的功能,我们姑且认为a这个字母比较靠前吧。awksed简单,它更像一门编程语言。

1.1、打印某一列

下面,这几行代码的效果基本是相同的:打印文件中的第一列。

#JavaSystem.out.println(aStr.split(" ")[0]);#Pythonprint(aString.split(" ")[0])#cut 命令cut -d " " -f1   file#awk 命令awk '{print $1}' file

这可能是 awk 最常用的功能了:打印文件中的某一列。它智能的去切分你的数据,不管是空格,还是TAB,大概率是你想要的。

对于 csv 这种文件来说,分隔的字符是,。AWK 使用-F参数去指定。以下代码打印 csv 文件中的第 1 和第 2 列。

awk -F ","  '{print $1,$2}' file

由此,我们可以看出一个基本的 awk 命令的组成部分。

在这里插入图片描述

一般的开发语言,数组下标是以 0 开始的,但 awk 的列$是以1开始的,而0指的是原始字符串。

1.2、网络状态统计

本小节,采用 awk 统计 netstat 命令的一些网络状态,来看一下 awk 语言的基本要素。netstat 的输出类似于:

在这里插入图片描述

其中,第 6 列,标明了网络连接所处于的网络状态。我们先给出 awk 命令,看一下统计结果。

netstat  -ant | awk ' \    BEGIN{print  "State","Count" }  \    /^tcp/ \    { rt[$6]++ } \    END{  for(i in rt){print i,rt[i]}  }'

输出结果为:

State CountLAST_ACK 1LISTEN 64CLOSE_WAIT 43ESTABLISHED 719SYN_SENT 5TIME_WAIT 146

下面这张图会配合以上命令详细说明,希望你能了解 awk 的精髓。

在这里插入图片描述

乍一看,好吓人的命令,但是很简单。awk 和我们通常的程序不太一样,它分为四个部分。

1、BEGIN 开头部分,可选的。用来设置一些参数,输出一些表头,定义一些变量等。上面的命令仅打印了一行信息而已。

2、END 结尾部分,可选的。用来计算一些汇总逻辑,或者输出这些内容。上面的命令,使用简单的 for 循环,输出了数组 rt 中的内容。

3、Pattern 匹配部分,依然可选。用来匹配一些需要处理的行。上面的命令,只匹配 tcp 开头的行,其他的不进入处理。

4、Action 模块。主要逻辑体,按行处理,统计打印,都可以。

注意点 1、awk 的主程序部分使用单引号‘包围,而不能是双引号 2、awk 的列开始的 index 是 0,而不是 1

1.3、例子

我们从几个简单的例子,来看下 awk 的作用。

1、输出 Recv-Q 不为 0 的记录

netstat -ant | awk '$2 > 0 {print}'

2、外网连接数,根据 ip 分组

netstat -ant | awk '/^tcp/{print $4}' | awk -F: '!/^:/{print $1}' | sort | uniq -c

3、打印 RSS 物理内存占用

top -b -n 1 | awk 'NR>7{rss+=$6}END{print rss}

4、过滤(去掉)空白行

awk 'NF' file

5、打印奇数行

awk 'a=!a' file

6、输出行数

awk 'END{print NR}' file

这些命令,是需要了解 awk 的一些内部变量的,接下来我们来介绍。

1.4、内置变量 FS

下面的两个命令是等价的 。

awk -F ':'  '{print $3}' fileawk 'BEGIN{FS=":"}{print $3}' file

BEGIN 块中的FS,就是内部变量,可以直接指定或者输出。如果你的文件既有用,分隔的,也有用:分割的,FS 甚至可以指定多个分隔符同时起作用。

FS="[,:|]"
其他

OFS 指定输出内容的分割符,列数非常多的时候,简化操作。相似命令:

awk -F ':' '{print $1,"-",$2,"-",$4}' fileawk 'BEGIN{FS=":";OFS="-"}{print $1,$2,$4}' file 

NF 列数。非常有用,比如,过滤一些列数不满足条件的内容。

awk -F, '{if(NF==3){print}}' file

NR 行号,例如,下面两个命令是等价的。

cat -n fileawk '{print NR,$0}' file

RS 记录分隔标志ORS 指定记录输出的分隔标志

FILENAME 当前处理的文件名称,在一次性处理多个文件时非常有用

1.5、编程语言特性 数学运算

从上面的代码可以看出,awk 可以做一些简单的运算。它的语言简洁,不需要显示的定义变量的类型。

比如上面的rt[$6]++,就已经默认定义了一个叫做 rt 的 hash(array?),里面的 key 是网络状态,而 value 是可以进行运算的(+-*/%)。

包含一些内置的数学运算(有限)

intlogsqrtexpsincosatan2randsrand
字符串操作

类似其他语言,awk 也内置了很多字符串操作函数。它本来就是处理字符串的,所以必须强大。

length(str) #获取字符串长度split(input-string,output-array,separator)substr(input-string, location, length)
语言特性

awk 是个小型的编程语言,看它的基本语法,如果你需要复杂一点的逻辑,请自行深入了解,包括一些时间处理函数:

# logicif(x=a){}if(x=a){}else{}while(x=a){break;continue;}do{}while(x=a)for(;;){}# arrayarr[key] = valuefor(key in arr){arr[key]}delete arr[key]asort(arr) #简单排序

据说,awk 可以胜任所有的文本操作。因为它本身就是一门语言啊。

1.6、AWK 总结

曾经使用 awk 编写过复杂的日志处理和统计程序。虽然比写sed舒畅了很多,但还是备受煎熬。更加上现在有各种 nawk,gawk 版本之间的区别,所以业务复杂度一增长,就习惯性的转向更加简洁、工具更全的 python。

awk 处理一些简单的文本还是极其方便的,最常用的还是打印某一列之类的,包括一些格式化输出。对于 awk,要简单的滚瓜烂熟,复杂的耳熟能详,毕竟有些大牛,就喜欢写这种脚本呢。

2、sed

sed命令应用广泛,使用简单,是快速文本处理的利器。它其实没多少技巧,背诵、使用是最合适的学习渠道,属于硬技能。但它又很复杂,因为高级功能太多。本篇不去关注 sed 的高级功能,仅对常用的一些操作,进行说明。

随着使用,你会发现它和vim的一些理念是想通的,正则表达式的语法也基本上一样,并没有多少学习成本。从个人视野和工作效率上来看,sed 命令都是程序员必须掌握的一个重要工具。

那些说可以现场 google 用法的,大多习惯将文本拷贝到 excel 里,慢慢磨洋工,遇到大批量文件更是手忙脚乱。不是一家人不进一家门,本文不是为你写的。

2.1、一个简单的入门

在这里插入图片描述

如图,一个简单的 sed 命令包含三个主要部分:参数范围操作。要操作的文件,可以直接挂在命令行的最后。除了命令行,sed 也可以通过-f 参数指定一个 sed 脚本,这个属于高级用法,不做过多描述。

有些示例命令我会重复多次,聪明如你一定能发现其中规律,有时连解释都用不着。

参数

-n 这个参数是--quiet或者--silent的意思。表明忽略执行过程的输出,只输出我们的结果即可。

我们常用的还有另外一个参数 :-i

使用此参数后,所有改动将在原文件上执行。你的输出将覆盖原文件。非常危险,一定要注意。

范围

1,4 表示找到文件中 1,2,3,4 行的内容。这个范围的指定很有灵性,请看以下示例(请自行替换图中的范围部分)。

5 选择第 5 行。 2,5 选择 2 到 5 行,共 4 行。 1~2 选择奇数行。 2~2 选择偶数行。 2,+3 和2,5的效果是一样的,共 4 行。 2,$ 从第二行到文件结尾。

范围的选择还可以使用正则匹配。请看下面示例。

/sys/,+3 选择出现 sys 字样的行,以及后面的三行。 /\^sys/,/mem/ 选择以 sys 开头的行,和出现 mem 字样行之间的数据。

为了直观,下面的命令一一对应上面的介绍,范围和操作之间是可以有空格的。

sed -n '5p' filesed -n '2,5 p' filesed -n '1~2 p' filesed -n '2~2 p' filesed -n '2,+3p' filesed -n '2,$ p' filesed -n '/sys/,+3 p' filesed -n '/^sys/,/mem/p' file
操作

最常用的操作就是p,意思就是打印。比如,以下两个命令就是等同的:

cat file sed -n 'p' file

除了打印,还有以下操作,我们来说常用的。

p 对匹配内容进行打印。 d 对匹配内容进行删除。这个时候就要去掉-n参数了,想想为什么。 w 将匹配内容写入到其他地方。

a,i,c等操作虽基本但使用少,不做介绍。我们依然拿一些命令来说明。

sed -n '2,5 p' filesed    '2,5 d' filesed -n '2,5 w output.txt' file

我们来看一下 sed 命令都能干些啥,上点命令体验一下。

删除所有#开头的行和空行。

sed -e 's/#.*//' -e '/^$/ d' file

最常用的,比如下面这个。

sed -n '2p' /etc/group

表示打印 group 文件中的第二行。

1、参数部分 比如 -n2、模式部分 比如'2p'3、文件,比如/etc/group

那么我想一次执行多个命令,还不想写 sed 脚本文件怎么办?那就需要加-e 参数。

sed 的操作单元是

2.2、替换模式

以上是sed命令的常用匹配模式,但它还有一个强大的替换模式,意思就是查找替换其中的某些值,并输出结果。使用替换模式很少使用-n参数。

在这里插入图片描述

替换模式的参数有点多,但第一部分和第五部分都是可以省略的。替换后会将整个文本输出出来。

前半部分用来匹配一些范围,而后半部分执行替换的动作。

范围

这个范围和上面的范围语法类似。看下面的例子。

/sys/,+3 选择出现 sys 字样的行,以及后面的三行。 /\^sys/,/mem/ 选择以 sys 开头的行,和出现 mem 字样行之间的数据。

具体命令为:

sed '/sys/,+3 s/a/b/g' filesed '/^sys/,/mem/s/a/b/g' file 
命令

这里的命令是指 s。也就是 substitute 的意思。

查找匹配

查找部分会找到要被替换的字符串。这部分可以接受纯粹的字符串,也可以接受正则表达式。看下面的例子。

a 查找范围行中的字符串a。 [a,b,c] 从范围行里查找字符串 a 或者 b 或者 c。

命令类似:

sed 's/a/b/g' filesed 's/[a,b,c]//g' file#这个命令我们下面解释
替换

是时候把找出的字符串给替换掉了。本部分的内容将替换查找匹配部分找到的内容。

可惜的是,这部分不能使用正则。常用的就是精确替换。比如把 a 替换成 b。

但也有高级功能。和 java 或者 python 的正则 api 类似,sed 的替换同样有Matched Pattern的含义,同样可以得到 Group,不深究。常用的替位符,就是&

&号,再重复一遍。当它用在替换字符串中的时候,代表的是原始的查找匹配数据。

[&] 表明将查找到的数据使用[]包围起来。 "&" 表明将查找的数据使用""包围起来。

下面这条命令,将会把文件中的每一行,使用引号包围起来。

sed 's/.*/"&"/' file
flag 参数

这些参数可以单个使用,也可以使用多个,仅介绍最常用的。

g 默认只匹配行中第一次出现的内容,加上 g,就可以全文替换了。常用。 p 当使用了-n参数,p将仅输出匹配行内容。 w 和上面的 w 模式类似,但是它仅仅输出有变换的行。 i 这个参数比较重要,表示忽略大小写。 e 表示将输出的每一行,执行一个命令。不建议使用,可以使用 xargs 配合完成这种功能。

看两个命令的语法:

sed -n 's/a/b/gipw output.txt' filesed 's/^/ls -la/e' file
好玩

由于正则的关系,很多字符需要转义。你会在脚本里做些很多\\\*之类的处理。你可以使用|^@!四个字符来替换\。比如,下面五个命令是一样的。

sed '/aaa/s/\/etc/\/usr/g' filesed '/aaa/s@/etc@/usr@g' filesed '/aaa/s^/etc^/usr^g' filesed '/aaa/s|/etc|/usr|g' filesed '/aaa/s!/etc!/usr!g' file

注意:前半部分的范围是不能使用这种方式的。我习惯使用符号@

2.3、其他 正则表达式

可以看到,正则表达式在命令行中无处不在。以下,紧做简要说明。

^ 行首 $ 行尾 . 单个字符 * 0 个或者多个匹配 + 1 个或者多个匹配 ? 0 个或者 1 个匹配 {m} 前面的匹配重复 m 次 {m,n} 前面的匹配重复 m 到 n 次 ** 转义字符 *[0-9]* 匹配括号中的任何一个字符,or 的作用 | or,或者 \b 匹配一个单词。比如\blucky\b 只匹配单词 lucky

参数 i

上面已经简单介绍了参数 i,它的作用是让操作在原文件执行。无论你执行了啥,原始文件都将会被覆盖。这是非常危险的。通过加入一个参数,可以将原文件做个备份。

sed -i.bak 's/a/b/' file

以上命令会对原 file 文件生效,并生成一个 file.bak 文件。强烈建议使用 i 参数同时指定 bak 文件。

表演一下

我们通过两个命令,来稍微看下 sed 和其他命令组合起来的威力。

输出长度不小于 50 个字符的行

sed -n '/^.{50}/p'

统计文件中有每个单词出现了多少次

sed 's/ /\n/g' file | sort | uniq -c

查找目录中的 py 文件,删掉所有行级注释

find ./ -name "*.py" | xargs sed  -i.bak '/^[ ]*#/d'

查看第 5-7 行和 10-13 行

sed -n -e '5,7p' -e '10,13p' file

仅输出 ip 地址

ip route show | sed -n '/src/p' | sed -e 's/  */ /g' | cut -d' ' -f9
3、sed 高级

sed 命令有两个空间,一个叫 pattern space,一个叫 hold space。这两个空间能够证明人类的脑瓜容量是非常小的,需要经过大量的训练和烧脑的理解,才能适应一些非常简单的操作。

不信看下面简单的命令,作用是,删除文件中最后两行。

sed 'N; $!P;$!D;$d' file

在上面,我们介绍了常用的 sed 命令和操作,而且使用了两张图来作为辅助。但可惜的是,这两张图,严格来说是不准确的 (比如 s 命令,只是其中的一个子集),即使它能够帮助初学者快速入门。

本篇属于 sed 的中级用法,常见在一些 sed 脚本中,在日常中应用并不多,但往往能够获得意想不到的效果。

3.1、原理 工作模式

这要从 sed 的工作模式来说起。在这里插入图片描述

按照我们读取一个文件的尿性,一般是持续循环读取。比如下面的 python 代码,print 代表 p 命令。

with open('file', 'r') as f:    for line in f.readlines():        print(line)

sed 命令在这之上,还缓冲了另外一个东西。那就是“上一行的内容” ,叫做 hold space。而当前行,叫做 patter space。用 python 简单表现一下:

hold_space = ""with open('file', 'r') as f:    for pattern_space in f.readlines():        print(hold_space,pattern_space)        hold_space = pattern_space

具体过程,大体与上面的代码类似,以上面的图为例。在这个例子中,hold space 不参与运算,是全程无感的:

1、 读取当前行 wtf.. 到 Pattern space

2、 执行命令p,这会打印出当前行

3、 把 Pattern space 的内容,赋值给 Hold space

4、 继续下一行的处理,循环这个过程

一个例子:x

但我想稍微操作一下这两个缓冲区。这个操作就是 swap,使用 x 表示,这也是一些文本编辑器的一贯尿性。

也就是,在 p 之前,我们加上了个 x。表示先将这两个缓冲区进行置换,然后再往下走。

在这里插入图片描述

hold_space = ""with open('file', 'r') as f:    for pattern_space in f.readlines():        swap(hold_space,pattern_space)        print(hold_space,pattern_space)        hold_space = pattern_space

让我我们来想象一下这个过程。

1、 刚开始,hold_space 的内容是空。然鹅,还没被填充,它就被使用了,和当前行进行了置换

2、 p 命令用在了置换后的缓冲区上,第一次打印出了空行,fuck

3、 继续嘟嘟嘟,现在到了最后一行,马上进行了置换,没机会打印就到了 hold_space 中了

4、 当前行,存放的是倒数第二行的数据,最后一行见光死,就永远没有机会面世了

我们当然有办法把它搞出来,比如,我执行偶数次的交换 x。

sed -n 'x;x;x;x;p' file

有木有一股骑着羊驼走天下的的感觉?

3.2、应用 举个栗子

你可能会想,我对这两个缓冲区交换,有什么用?接下来看这个文件。

小姐姐味道公众号CEO加菲猫经理IT Manager系统毁灭师Sysadmin小哥哥味道Developer爱卖东西的经理Sales Manager风清扬Dog

文件奇数行是名称,偶数行是职位。我们想要输出所有Manager的名字。就可以使用下面的命令。

sed -n -e 'x;n' -e '/Manager/{x;p}' file

命令分为两个部分。

x;n 表示将偶数行保存在 pattern space,那么奇数行就保存在 hold space 中。

/Manager/{x;p} 命令将在 pattern space 上执行对 Manager 关键字的查找。如果符合条件,则再次交换 p 和 h 缓冲区,输出奇数行对应的名字。

上面的xn,就是针对这两个缓冲区的命令。这样的命令有很多。

命令

这些命令,如果多了,可以使用{}包围起来,就像上面的命令一样。这些命令的位置与我们上一篇所说的,在同一个地方。

常用的

x 请容许我用英文装个 b:Exchange the contents of the hold and pattern spaces.

d 清空当前的 pattern space,然后进入下一个循环

D 删除 pattern space 的第一行(multiline pattern)

h 复制 pattern space 到 hold space

H 追加 pattern spaced 到 hold space

g 复制 hold space 到 pattern space

G 追加 hold space 到 pattern space

n 读取下一个输入行到 pattern space

N 追加下一个输入行到 pattern space,同时将两行看做一行,但是两行之间依然含有\n 换行符

p 打印当前的 pattern space

P 打印当前的 pattern space 中的第一行

不常用的

上次提到的推箱子游戏,就用了很多这种东西。为了使使用者在书写 sed 脚本的时候真正的"自由",sed 还允许在脚本中用":"设置记号。标签,有种类似编程语言的特性了。

q 退出 sed,可以增加执行速度

l 列出当前行,包含不可打印字符

l width 列出当前行,使用一个width characters结尾

b label 跳到相应的标签,分之命令。

t label if 分支,从最后一行开始,条件一旦满足或者 T,t 命令,将导致分支到带有标号的命令处,或者到脚本的末尾。测试命令。

T label 错误分支,从最后一行开始,一旦发生错误或者 T,t 命令,将导致分支到带有标号的命令处,或者到脚本的末尾。

当然还有其他更不常用的,可以使用 man 命令查看

man sed
3.3、一些命令:开启训练模式

看着一行行进行处理,好像很简单是不是?不可能的,看下面几个简单的命令,训练一下生锈的脑子。

一个流水线一样的命令

sed -n '2{h;n;x;H;x};p' file

交换第 2 行和第 3 行的内容

输出最后一行

sed 'N;D' file

输出文件中最后两行

sed '$!N; $!D' file

删除文件中最后两行

sed 'N; $!P;$!D;$d' file

打印偶数行的另一种写法

sed –n 'n;p' file

每隔 5 行加入一个空行。

sed 'n;n;n;n;G' file

输出含 AAA 和 BBB 和 CCC(任意顺序)的段落

sed -e '/./{H; $!d;}' -e 'x;/AAA/!d; /BBB/!d; /CCC/!d' file

颠倒行序(使末行变首行,首行变末行)

sed -n '1!G; h; $p' file

这个命令有点绕,首先,1!G对除了第一行的其他行进行了 G 操作,然后反向复制回去,到了最后一行,就直接打印 Pattern Space。$表示到了最后一行执行下面的命令,也可以是${p}

一个带标签的用法

sed -e :a -e '$q;N;11,$D;ba'

打印最后 10 行。a是标签。语法就是单独的行,使用:分隔。

3.4、sed 小结

为了提高你在公司的竞争力,你是可以弄一堆 sed 脚本唬人(埋雷)的。和某些 perl 脚本一样,即使是有相关经验的开发着,理解起来也要下点功夫,就不要说其他人了。

这就是 sed,简约但不简单的命令,本文算是一个中级入门。中级入门也有点烧脑,因为你的脑瓜里,需要一直维护着这两个缓冲区。又是置换,又是清空,相当于人肉状态机。当然,怎么把这个过程讲的尽量简单一点,还是浪费了作者不少脑细胞的。哪怕你点个赞,也是延缓小姐姐走向老年痴呆时间的一个途径。有了你的支持,小姐姐也可以想点技术之外的事情,比如喷喷 bat 什么的。

学习方式

多多练习。一件事情重复上 3 次以上,就尝试着用脚本去实现吧,经过日积月累的努力,你会受益于这种从实践中得来的知识积累。

阅读全文: http://gitbook.cn/gitchat/activity/5e9ea7056e10e519aa235b7c

您还可以下载 CSDN 旗下精品原创内容社区 GitChat App ,阅读更多 GitChat 专享技术内容哦。

FtooAtPSkEJwnW-9xkCLqSTRpBKX

关注
打赏
1688896170
查看更多评论

蔚1

暂无认证

  • 4浏览

    0关注

    4645博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文
立即登录/注册

微信扫码登录

0.0474s