sed、awk 是工作中常用的命令,可显著提高生产效率,也是面试的常客。今天我们就以一篇长文,讲解这两个命令的常见使用,让你的工作如虎添翼。
sed 和 awk 是 Linux 上常用的命令,可以显著增加工作效率。但由于一些文章描述的很是晦涩,让人望而生畏,很多同学错过了这些工具,很是可惜。我这里总结了一些常用的操作,希望能帮你快速掌握这两个命令。
1、AWK敢用自己的名字做软件名字的,都有非常强大的自信。比如,垠语言什么的。
awk
的命名得自于它的三个创始人姓别的首字母,都是80 来岁
的老爷爷了。当然也有四个人的组合:流行的 GoF 设计模式。但对于我这游戏爱好者来说,想到的竟然是三位一体,果然是不争气啊。
它长的很像 C,为什么这么有名,除了它强大的功能,我们姑且认为a
这个字母比较靠前吧。awk
比sed
简单,它更像一门编程语言。
下面,这几行代码的效果基本是相同的:打印文件中的第一列。
#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
指的是原始字符串。
本小节,采用 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,要简单的滚瓜烂熟,复杂的耳熟能详,毕竟有些大牛
,就喜欢写这种脚本呢。
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 的操作单元是行
。
以上是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
注意:前半部分的范围是不能使用这种方式的。我习惯使用符号@
。
可以看到,正则表达式在命令行中无处不在。以下,紧做简要说明。
^ 行首 $ 行尾 . 单个字符 * 0 个或者多个匹配 + 1 个或者多个匹配 ? 0 个或者 1 个匹配 {m} 前面的匹配重复 m 次 {m,n} 前面的匹配重复 m 到 n 次 ** 转义字符 *[0-9]* 匹配括号中的任何一个字符,or 的作用 | or,或者 \b 匹配一个单词。比如\blucky\b
只匹配单词 lucky
上面已经简单介绍了参数 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 缓冲区,输出奇数行对应的名字。
上面的x
和n
,就是针对这两个缓冲区的命令。这样的命令有很多。
这些命令,如果多了,可以使用{}包围起来,就像上面的命令一样。这些命令的位置与我们上一篇所说的,在同一个地方。
常用的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
是标签。语法就是单独的行,使用:
分隔。
为了提高你在公司的竞争力,你是可以弄一堆 sed 脚本唬人(埋雷)的。和某些 perl 脚本一样,即使是有相关经验的开发着,理解起来也要下点功夫,就不要说其他人了。
这就是 sed,简约但不简单的命令,本文算是一个中级入门。中级入门也有点烧脑,因为你的脑瓜里,需要一直维护着这两个缓冲区。又是置换,又是清空,相当于人肉状态机。当然,怎么把这个过程讲的尽量简单一点,还是浪费了作者不少脑细胞的。哪怕你点个赞,也是延缓小姐姐走向老年痴呆时间的一个途径。有了你的支持,小姐姐也可以想点技术之外的事情,比如喷喷 bat 什么的。
学习方式多多练习。一件事情重复上 3 次以上,就尝试着用脚本去实现吧,经过日积月累的努力,你会受益于这种从实践中得来的知识积累。
阅读全文: http://gitbook.cn/gitchat/activity/5e9ea7056e10e519aa235b7c
您还可以下载 CSDN 旗下精品原创内容社区 GitChat App ,阅读更多 GitChat 专享技术内容哦。