第1章 什么是正则表达式
- 正则表达式就是为了处理大量的文本|字符串而定义的一套规则和方法
- 通过定义的这些特殊符号的辅助,系统管理员就可以快速过滤,替换或输出需要的字符串。Linux正则表达式一般以行为单位处理。
简单说
- 为处理大量文本|字符串而定义的一套规则和方法
- 以行为单位出来,一次处理一行
正则表达式是一种描述一组字符串的模式,类似数字表达式,通过各种操作符组成更小的表达式
第2章 为何使用正则表达式
linux运维工作,大量过滤日志工作,化繁为简。
简单,高效。
正则表达式高级工具;三剑客都支持
第3章 容易混淆的两个注意事项
- 正则表达式应用非常广泛,存在于各种语言中,php perl grep sed awk 支持。ls * 通配符
- 但现在学的是Linux中的正则表达式,最常应用正则表达式的命令是grep(egrep),sed,awk。
- 正则表达式和通配符有本质区别
正则表达式用来找:【文件】内容,文本,字符串。一般只有三剑客支持
通配符用来找:文件名,普通命令都支持
第4章 正则表达式使用注意事项
-
linux正则表达式以行为单位处理字符串
-
便于区别过滤出来的字符串,一定配合grep/egrep命令学习。
- 注意字符集,exportLC_All=C:无论何时,做何事都要注意字符集
第5章 正则表达式的分类
POSIX规范将正则表达式的分为了两种
- 基本正则表达式(BRE,basic regular expression)
- 高级功能:扩展正则表达式(ERE,extended regular expression)
5.1 BRE和ERE的区别仅仅是元字符的不同:
- BRE(基础正则表达式)只承认的元字符有^$.[]*其他字符识别为普通字符:\(\)
- ERE(扩展正则表达式)则添加了(){}?+|等
- 只有在用反斜杠“”进行转义的情况下,字符(){}才会在BRE被当作元字符处理,而ERE中,任何元符号前面加上反斜杠反而会使其被当作普通字符来处理。
第6章 如何区分通配符和正则表达式
- 不需要思考的判断方法:在三剑客awk,sed,grep,egrep都是正则,其他都是通配符
- 区别通配符和正则表达式最简单的方法:
(1)文件目录名===>通配符
(2)文件内容(字符串,文本【文件】内容)===>正则表达式
- 通配符和正则表达式都有“*”,“?”,“【】”,但是通配符的这些符号都能自身代表任意字符,而正则表达式的这些符号只能代表这些符号前面的字符
第7章 基本正则表达式
7.1 基础正则表达式
字符 | 描述 |
---|---|
^ | ^word搜索以word开头的内容 |
$ | word$搜索以word结尾的内容 |
---|
^$ | 表示空行,不是空格 |
---|
. | 代表且只能代表任意一个字符(不匹配空行) |
---|
\ | 转义字符,让有特殊含义的字符脱掉马甲,现出原形,如\.只表示小数点 |
---|
* | 重复之前的字符或文本0个或多个,之前的文本或字符连续0次或多次 |
---|
.* | 任意多个字符 |
---|
^.* | 以任意多个字符串开头,.*尽可能多,有多少算多少,贪婪性 |
---|
括号表达式 | |
---|---|
[abc][0-9][\.,/] | 匹配字符集合内的任意一个字符a或b或c:[a-z]匹配所有小写字母;表示一个整体,内藏无限可能;[abc]找a或b或c可以写成[a-c] |
[^abc] | 匹配不包含^后的任意字符a或b或c,是对[abc]的取反,且与^含义不同 |
---|
a\{n,m\} | 重复前面a字符n到m次(如果用egrep或sed -r可去掉斜线) |
---|
a\{n,\} | 重复前面a字符至少n次,如果用egrep或sed -r可去掉斜线 |
---|---|
a\{n\} | 重复前面a字符n次,如果用egrep或sed -r可去掉斜线 |
--- | --- |
第8章 扩展正则表达式ERE
特殊字符 | 含义与例子 |
---|---|
+ | 重复前一个字符一次或一次以上,前一个字符连续一个或多个,把连续的文本/字符取出 |
? | 重复前面一个字符0次或1次(.是有且只有1个) |
---|
管道符 | 表示或者同时过滤多个字符 |
---|
() | 分组过滤被括起来的东西表示一个整体(一个字符),后向引用 |
---|
第9章 正则小结
-
基础正则:BRE
|^|$|.||.|[abc]|[^abc]|
|---|---| -
扩展正则:ERE
|+|||?|()|{}|a{n,m}|a{n,}|a{n}|
|---|---| -
转义字符\:将字符的意思改变(不支持正则符号的,转变字符含义为正则,支持正则的转变为普通字符含义)
注意:
- grep默认不支持正则,因此正则表达式的符号对于grep来说就等同于普通字符含义,因此,想让grep直接处理正则符号必须通过转义字符\{\}来处理。
- grep -E 强制让grep直接认识正则符号,不需要再进行转义
- egrep 等效grep -E 天生就能认识正则符号
- 我们平时备份可以通过cp 文件名{,.bak}的形式进行,避免再打一次文件名
sed -r :让sed支持正则
第10章 基本正则和扩展正则区别
基础正则BRE | 扩展正则ERE |
---|---|
\? | ? |
\+ | + |
\{\} | {} |
\( \ ) | () |
\ |
所谓基础正则实际上就是得需要转义字符配合表达的正则,而扩展正则就是让命令扩展它的权限让他直接就认识正则表达符号(egrep,sed -r,awk直接支持)
第11章 补充说明
11.1 一些预定义的:
正则表达式 | 描述 | 示例 |
---|---|---|
[:alnum:] | [a-zA-Z0-9]匹配任意一个字母或数字字符 | [[:alnum:]]+ |
[:alpha:] | 匹配任意一个字母字符(包括大小写字母) | [[:alpha:]]{4} |
[:blank:] | 空格与制表符(横向纵向) | [[:blank:]]* |
[:digit:] | 匹配任意一个数字字符 | [[:digit:]]? |
[:lower:] | 匹配小写字母 | [[:lower:]]{5,} |
[:upper:] | 匹配大写字母 | ([[:upper:]]+)? |
[:punct:] | 匹配标点符号 | [[:punct:]] |
[:space:] | 匹配一个包括换行符,回车等在内的所有空白符 | [[:space:]]+ |
[:graph:] | 匹配任何一个可以看得见的且可以打印的字符 | [[:graph:]] |
[:xdigit:] | 任何一个十六进制数 | [[:xdigit:]]+ |
[:cntrl:] | 任何一个控制字符(ASCII字符集中的前32个字符) | [[:cntrl:]] |
[:print:] | 任何一个可以打印的字符 | [[:print:]] |
11.2 元字符
元字符是一种Perl风格的正则表达式,只有一部分文本处理工具支持它,并不是所有的文本处理工具都支持
正则表达式 | 描述 | 示例 |
---|---|---|
\b | 单词边界 | \bcool\b匹配cool,不匹配coolant |
\B | 非单词边界 | cool\B匹配coolant不匹配cool |
\d | 单个数字字符 | b\db匹配b2b,不匹配bcb |
\D | 单个非数字字符 | b\Db匹配bcb不匹配b2b |
\w | 单个单词字符(字母,数字与_) | \w匹配1或a,不匹配& |
\W | 单个非单词字符 | \W匹配&,不匹配1或a |
\n | 换行符 | \n匹配一个新行 |
\s | 单个空白字符 | x\sx匹配xx,不匹配xx |
\S | 单个非空白字符 | x\S\x匹配xkx,不匹配xx |
\r | 回车 | \r匹配回车 |
\t | 横向制表符 | \t匹配一个横向制表符 |
\v | 垂直制表符 | \v匹配一个垂直制表符 |
\f | 换页符 | \f匹配一个换页符 |
第12章 正则表达式总结
- egrep/grep 了解一下正则,简单看看效果,结果
- egrep/grep -o 参数看正则到底匹配了什么
- 多练就好,配合grep,egrep,sed -r,awk更为强大
第十二节 linux三剑客之sed命令精讲
标签(空格分隔): Linux实战教学笔记-陈思齐
---更多资料点我查看
1,前言
- 我们都知道,在Linux中一切皆文件,比如配置文件,日志文件,启动文件等等。如果我们相对这些文件进行一些编辑查询等操作时,我们可能会想到一些vi,vim,cat,more等命令。但是这些命令效率不高,这就好比一块空地准备搭建房子,请了10个师傅拿着铁锹挖地基,花了一个月的时间才挖完,而另外一块空地则请了个挖土机,三下五除二就搞定了,这就是效率。而在linux中的“挖土机”有三种型号:顶配awk,中配sed,标配grep。使用这些工具,我们能够在达到同样效果的前提下节省大量的重复性工作,提高效率。
- 接下来我们就看一下sed的详细说明
- sed 是Stream Editor(字符流编辑器)的缩写,简称流编辑器。什么是流?大家可以想象以下流水线,sed就像一个车间一样,文件中的每行字符都是原料,运到sed车间,然后经过一系列的加工处理,最后从流水线下来就变成货物了。
- 当然上图中的文件内容可以是来自文件,也可以直接来自键盘或者管道等标准输入,最后的结果默认情况下是显示到终端的屏幕上,但是也可以输出到文件中。
- 以前工厂中没有流水线时,生产一件商品需要十几个工种互相配合,这样下来利润太低,后来就有了流水线,生产一件商品虽然还是有十几道工序,但都是机器化生产,工人只是辅助作用,这样利润就大大提高了,产量也大大提高了。
- 编辑文件也是这样,以前我们修改一个配置文件,需要移动光标到某一行,然后添加点文字,然后又移动光标到另一行,注释点东西.......可能修改一个配置文件下来需要花费数十分钟,还有可能改错了配置文件,又得返工。这还是一个配置文件,如果数十个数百个呢?因此当你学会了sed命令,你会发现利用它处理文件中的一系列修改是很有用的。只要想到在大约100多个文件中,处理20个不同的编辑操作可以在几分钟之内完成,你就会知道sed的强大了。
2,软件功能与版本
- Sed命令是操作,过滤和转换文本内容的强大工具。常用功能有增删改查(增加,删除,修改,查询),其中查询的功能中最常用的2大功能是过滤(过滤指定字符串),取行(取出指定行)。
- 我们现在准备学习的sed版本是GNU开源版本的,我的实验环境是CentOS6.8系统,内核版本是2.6.32-642.el6.x86_64
[root@chensiqi1 ~]# cat /etc/redhat-release
CentOS release 6.8 (Final)
[root@chensiqi1 ~]# uname -r
2.6.32-642.el6.x86_64
[root@chensiqi1 ~]# sed --version # 查看sed软件版本
GNU sed version 4.2.1
3,语法格式
sed [options] [sed -commands][input -file]
sed [选项] 【sed命令】 【输入文件】
说明:
1,注意sed软件以及后面选项,sed命令和输入文件,每个元素之间都至少有一个空格。
2,为了避免混淆,本文称呼sed为sed软件。sed -commands(sed命令)是sed软件内置的一些命令选项,为了和前面的options(选项)区分,故称为sed命令
3,sed -commands 既可以是单个sed命令,也可以是多个sed命令组合。
4,input -file (输入文件)是可选项,sed还能够从标准输入如管道获取输入。
4,命令执行流程
概括流程:
Sed软件从文件或管道中读取一行,处理一行,输出一行;再读取一行,再处理一行,再输出一行....
小知识:
一次一行的设计使得sed软件性能很高,sed在读取非常庞大的文件时不会出现卡顿的想象。大家都用过vi命令,用vi命令打开几十M或更大的文件,会发现有卡顿现象,这是因为vi命令打开文件是一次性将文件加载到内存,然后再打开,因此卡顿的时间长短就取决于从磁盘到内存的读取速度了。而且如果文件过大的话还会造成内存溢出现象。Sed软件就很好的避免了这种情况,打开速度非常快,执行速度也很快。
详细流程:
现有一个文件person.txt,共有五行文本,sed命令读入文件person.txt的第一行“101,chensiqi,CEO”,并将这行文本存入模式空间(sed软件在内存中的一个临时缓存,用于存放读取到的内容,比喻为工厂流水线的传送带。)
文件person.txt在模式空间的完整处理流程
1,判断第1行是否是需要处理的行,如果不是要处理的行就重新从文件读取下一行,如果是要处理的行,则接着往下走。
2,对模式空间的内容执行sed命令,比如a(追加),i(插入),s(替换)...
3,将模式空间中经过sed命令处理后的内容输出到屏幕上,然后清空模式空间
4,读取下一行文本,然后重新执行上面的流程,直到文件结束
上面的流程概括如下图所示:
Sed软件有两个内置的存储空间:
- 模式空间(pattern space):是sed软件从文本读取一行文本然后存入的缓冲区(这个缓冲区是在内存中的),然后使用sed命令操作模式空间的内容。
- 保持空间(hold space):是sed软件另外一个缓冲区,用来存放临时数据,也是在内存中,但是模式空间和保持空间的用途是不一样的。Sed可以交换保持空间和模式空间的数据,但是不能在保持空间上执行普通的sed命令,也就是说我们可以在保持空间存储数据。
5,选项说明
option[选项] | 解释说明(带*的为重点) |
---|---|
-n | 取消默认的sed软件的输出,常与sed命令的p连用。* |
-e | 一行命令语句可以执行多条sed命令 |
-f | 选项后面可以接sed脚本的文件名 |
-r | 使用扩展正则表达式,默认情况sed只识别基本正则表达式* |
-i | 直接修改文件内容,而不是输出到终端,如果不使用-i选项sed软件只是修改在内存中的数据,并不会影响磁盘上的文件* |
sed -commands[sed命令] | 解释说明(带*的为重点) |
---|---|
a | 追加,在指定行后添加一行或多行文本* |
c | 取代指定的行 |
d | 删除指定的行* |
D | 删除模式空间的部分内容,直到遇到换行符\n结束操作,与多行模式相关 |
i | 插入,在指定行前添加一行或多行文本* |
h | 把模式空间的内容复制到保持空间 |
H | 把模式空间的内容追加到保持空间 |
g | 把保持空间的内容复制到模式空间 |
G | 把保持空间的内容追加到模式空间 |
x | 交换模式空间和保持空间的内容 |
l | 打印不可见的字符 |
n | 清空模式空间的内容并读入下一行 |
N | 不清空模式空间,并读取下一行数据并追加到模式空间* |
p | 打印模式空间内容,通常p会与选项-n一起使用* |
P(大写) | 打印模式空间的内容,直到遇到换行符\n结束操作 |
q | 退出Sed |
r | 从指定文件读取数据 |
s | 取代,s#old#new#g==>这里g是s命令的替代标志,注意和g命令区分。* |
w | 另存,把模式空间的内容保存到文件中 |
y | 根据对应位置转换字符 |
:label | 定义一个标签 |
b label | 执行该标签后面的命令 |
t | 如果前面的命令执行成功,那么就跳转到t指定的标签处,继续往下执行后续命令。否则,仍然继续正常的执行流程 |
特殊符号 | 解释说明(带*的为重点) |
---|---|
! | 对指定行以外的所有行应用命令* |
= | 打印当前行行号 |
~ | “First~step”表示从First行开始,以步长Step递增 |
& | 代表被替换的内容 |
: | 实现一行命令语句可以执行多条sed命令* |
{} | 对单个地址或地址范围执行批量操作 |
+ | 地址范围中用到的符号,做加法运算 |
6,使用范例
6.1 统一实验文本
为了更好的测试sed命令的用法,我们需要准备好下面的测试文件。
[root@chensiqi1 ~]# cat >person.txt<<KOF
> 101,chensiqi,CEO
> 102,zhangyang,CTO
> 103,Alex,COO
> 104,yy,CFO
> 105,feixue,CIO
> KOF #KOF必须成对出现,表示终止输入
命令说明:使用一条cat命令创建多行文本,文件包含上面的内容,后面的操作都会使用这个文件。
6.2 常用功能-增删改查
接下来我们就开始学习sed命令的“四斧头”-->增,删,改,查。大家也不要着急,我们一个一个的学过去,热豆腐要慢慢的吃。
6.2.1 增
- 接下来我来教大家第一式招法-->往文件指定位置追加或插入指定文本。
- 这个功能非常有用,比如我们平时往配置文件写入几行文本,最常用的是vi或vim命令,但是这2个命令是一种交互式的命令,还需要我们在vi/vim编辑器界面输入字符串然后保存退出,操作有些繁琐但是还能用。但是当我们学会了Shell脚本后,我们就会发现在脚本中不能正常使用vi或vim命令,为什么呢?同学们请自行体验。
- 我们学习Shell脚本主要是为了解放我们的双手,执行一个脚本,然后自动往文件中写入数据,不需要我们再动手。因此我们想到了sed软件,它能够帮助我们实现目的。
- 这里我们需要用到2个sed命令,分别是:
“a”:追加文本到指定行后,记忆方法:a的全拼是apend,意思是追加。
“i“:插入文本到指定行前,记忆方法:i的全拼是insert,意思是插入。
6.2.1.1 单行增
首先我们看一下单行增加的用法,说白了就是在文件中增加一行文本,我们以前学过echo命令可以在文件的末尾追加文本,比较简单,但是我们还有其他的复杂需求,比如在第10行插入一行数字等等,这里就需要sed出马了。我们来看一下下面的例子,同学也跟着例子一起练习,命令只有多练才会熟悉,光看永远是学不会的。
[root@chensiqi1 ~]# sed '2a 106,dandan,CSO' person.txt
101,chensiqi,CEO
102,zhangyang,CTO
106,dandan,CSO #这就是新增那句
103,Alex,COO
104,yy,CFO
105,feixue,CIO
命令行详解:
首先我们看一下命令的结果,我们可以看到原来的第二行“102,zhangyang,CTO” 前面插入了新的一行为“106,dandan,CSO”,原来的第2行变成第3行了。
接下来我们解读一下sed语句的结构,sed打头,然后接上空格(空格个数不限,但至少要有一个!)在空格后面,我们先敲上一对单引号(“)然后退格在单引号中协商‘106,dandan,CSO’。
- 2代表指定对第2行操作,其他的行忽略
- i代表插入的意思,2i即在第2行前插入文本
- 2i后面加上空格,然后跟上你想要插入的文本即可
最后接上你想要处理的文件person.txt
6.2.1.2 引号的区别总结
在教学过程中,有同学问我在sed中到底使用单引号还是双引号?这里给大家详细说说引号的区别。
- 双引号:把双引号的内容输出出来;如果内容中有命令,变量等,会先把命令,变量解析出结果,然后再输出最终内容来。双引号内命令或变量的写法为`命令或变量`或$(命令或变量)
- 单引号:所见即所得,将单引号内的内容原样输出,阻止所有字符的转义
- 不加引号:不会将含有空格的字符串视为一个整体输出,如果内容中有命令,变量等,会先把命令,变量解析出结果,然后再输出最终内容来,如果字符串含有空格等特殊字符,则不能完整输出,则需改加双引号。
- 倒引号(反引号Esc键下方):进行命令的替换,在倒引号内部的shell命令将会被执行,其结果输出代替用倒引号括起来的文本。
Sed为何用单引号?
[root@chensiqi1 ~]# cat person.txt
101,chensiqi,CEO
102,zhangyang,CTO
103,Alex,COO
104,yy,CFO
105,feixue,CIO
[root@chensiqi1 ~]# sed '2i $PATH' person.txt #单引号--文本内容原封不动插入
101,chensiqi,CEO
$PATH
102,zhangyang,CTO
103,Alex,COO
104,yy,CFO
105,feixue,CIO
[root@chensiqi1 ~]# sed 2i $PATH person.txt #不加引号,linux无法辨认空格,不会把有空格的命令当成一条命令来执行
sed: -e expression #1, char 2: expected \ after `a', `c' or `i'
[root@chensiqi1 ~]# sed "2i $PATH" person.txt #双引号--变量$PATH被解析以后在当作文本进行插入
101,chensiqi,CEO
/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
102,zhangyang,CTO
103,Alex,COO
104,yy,CFO
105,feixue,CIO
结论一:
- 如果引号里面是普通字符串的话,你可以任意使用单引号或者双引号:
- 如果引号里面是变量或者带反引号的命令的话,你想要变量解析的结果或者命令执行的结果,那就使用双引号;你想要引号内的原样字符串,那就使用单引号。
- 只要大家理解上面的用法,明白自己想要什么,那么你想用什么引号就用什么引号。
- 其实就二选一,非此即彼,这个不行就试试那个。
结论二:
- 很多时候,总结的结论未必适合所有情况。但是,总结小结论,便于我们学习知识和对知识的深刻理解,因此推荐大家多做总结。
- 不要太纠结于结论,结论毕竟不是真理,只是我们学习过程的产物。就像数学里的平行线一样,在初中定义就是不相交的两条直线就是平行线,到了高中结论就是错的了。
6.2.1.3 多行增
前面学习的内容已经实现了往文件追加或插入单行文本,但是还有插入多行文本的需求,我们也学习过cat命令能够往文件中追加多行文本。
示例准备,请执行如下完整命令生成test.txt文件内容:
[root@chensiqi1 ~]# cat > test.txt <<EOF
> welcome to my blog.http://www.cnblogs.com/chensiqiqi/
>
> if you like my blog\'s contents,pls support me.
>
>
>
> bye!boys and girls.
> EOF
命令说明:以上就是cat的生产环境常用的生成或为文件追加内容的方法,请不要忽略上文中的空行。另外,如果内容中有单引号,$符号等特殊符号作为内容时要用“\”转义。
以上命令执行过程及结果:
[root@chensiqi1 ~]# cat > test.txt <<EOF
> welcome to my blog.http://www.cnblogs.com/chensiqiqi/
> #此行是空行
> if you like my blog\'s contents,pls support me.
> #此行是空行
> #此行是空行
> #此行是空行
> bye!boys and girls.
> EOF #-->这里要敲回车才能结束,另外,EOF必须成对出现,但也可以用别的成对标签替换,例如:chensiqi字符标签。
命令说明:上面命令结果最左侧的“>”是Linux系统自动生成的,不是我们手动敲的。
- ok,我们前面已经简单的回顾了cat命令的用法,如果大家想详细了解cat命令的详细用法,请持续关注我的博客。
- 前面我们实现了用cat命令往文件中追加多行文本,但是cat命令和echo命令有同样的缺点,只能是往文末添加内容,并不能够在指定行操作,因此还是需要我们的sed命令出马。
- 首先我们需要了解一个特殊符号“\n”,这个符号叫做换行符,顾名思义换行。我们看几个例子来认识一下这个符号。
[root@chensiqi1 ~]# echo "chensiqi";echo "chensiqi"
chensiqi
chensiqi
命令说明:上面的命令用分号“;”连接了两条命令,然后输出结果为2行chensiqi,但是有没有更简单的方法呢?
[root@chensiqi1 ~]# echo -e "chensiqi\nchensiqi"
chensiqi
chensiqi
命令说明:这里就“\n”派上用场了,行与行之间是以“\n”作为分隔符的,所以“chensiqi\nchensiqi”就等效于2行chensiqi。接下来我们用echo命令实验一下,其中-e参数表示字符串中如果出现以下特殊字符(\n代表换行,\t代表Tab键等),则加以特殊处理,而不会将它当成一般文字输出。如果大家想详细了解echo命令的详细用法,请持续关注我的博客。
学习完换行符,我们来看一下sed命令如何使用
!
[root@chensiqi1 ~]# sed '2a 106,dandan,CSO\n107,bingbing,CCO' person.txt
101,chensiqi,CEO
102,zhangyang,CTO
106,dandan,CSO #同时追加多行
107,bingbing,CCO #同时追加多行
103,Alex,COO
104,yy,CFO
105,feixue,CIO
EOF
命令行分析:
- 我们首先粗略扫一眼这个命令语句,可以发现命令结构是和单行增加文本是几乎没有区别的。
- 然后我们看一下命令的结果,我们可以看到原来的第二行“102,zhangyang,CTO”后面追加了2行文本“106,dandan,CSO”和“107,bingbing,CCO”。
- 接下来我们解读一下sed语句的结构,sed软件打头,然后接上空格,在空格后面,我们先敲上一对单引号(“),然后退一格在单引号中写上‘2a 106,dandan,CSO\n107,bingbing,CCO’。
- 2代表指定对第2行操作,其他的行忽略;
- a代表追加的意思,2a即在第2行后追加文本;
- 2a后面加上空格,然后跟上你想要插入的多行文本即可。这里的每行文本使用“\n”连接就可以写成一行了。
最后接上你想要处理的文件person.txt
当然还有另外一种方法添加多行文本,但这种方法并没有“\n”方便,所以在这里就简单说一下。这种方法利用了“”,它也有换行的意思。如果大家在执行一行很长的命令时候,如果都写在一行,那太难看了也难以理解,因此就利用到了这个符号可以将一条完整的命令分成多行,举个例子:
[root@chensiqi1 ~]# echo chensiqi
chensiqi
[root@chensiqi1 ~]# echo \
> chensiqi
chensiqi
[root@chensiqi1 ~]# echo \
> chensiqi \
> is \
> me
chensiqi is me
命令说明:一条命令可以通过\符号进行多行输出
接着说一下sed命令使用反斜线,首先输入完“sed '2a 106,dandan,CSO ”,然后敲回车键,这样窗口会显示一个符号“>”,我们在这个符号后面接着写命令的剩余部分“107,bingbing,CCO‘ person.txt”。
[root@chensiqi1 ~]# sed '2a 106,dandan,CSO \
> 107,bingibng,CCO' \
> person.txt
101,chensiqi,CEO
102,zhangyang,CTO
106,dandan,CSO
107,bingibng,CCO
103,Alex,COO
104,yy,CFO
105,feixue,CIO
EOF
命令说明:结果和第一种方法是一样的,但是我们也发现这种方法比较麻烦,因此建议大家使用第一种方法。
- sed软件使用命令i插入多行文本和命令和a的用法是一样的,因此这里不再详细列出,大家可以练习一下,就是把前面命令的“a”换成“i”。
- 学习了这么多内容,总要有用武之地,因此大家不要看答案先独自完成下面的企业案例!
企业案例1:优化SSH配置(一键完成增加若干参数)
在我们学习CentOS6系统优化时,有一个优化点:更改ssh服务远程登录的配置。主要的操作是在ssh的配置文件/etc/ssh/sshd_config加入下面5行文本。(下面参数的具体含义见其他课程。)
Port 52113
PermitRootLogin no
PermitEmptyPasswords no
UseDNS no
GSSAPIAuthentication no
当然我们可以使用vi/vim命令编辑这个文本,但是这样就比较麻烦,现在想用一条命令增加5行文本到第13行前?
注意:修改前别忘了备份配置文件:cp /etc/ssh/sshd_config{,.bak}
上面的命令知识要是不懂请看通配符章节
这道企业面试题可以用我们学过的sed命令多行追加功能就可以搞定。
[root@chensiqi1 ~]# sed -i '13i Port 52113\nPermitRootLogin no\nPermitEmptyPasswords no\nUseDNS no\nGSSAPIAuthentication no' /etc/ssh/sshd_config
命令说明:题目要求在第13行前插入,那就需要使用命令13i。有同学做个题目时,是这样想的,在13行前,那不就是12行后吗,12a也是可以的。是的,这样也是没错的,这可以算是第二种方法。
最后插入的5行内容使用“\n”就可以变成一行了。
上面还有一个没讲过的选项"-i",这个选项能够实际的修改文件内容,大家练习时可以去掉,防止改掉了配置文件。如果使用了-i,可以用备份文件还原。当然,在生产环境修改配置文件那就需要用-i选项了。
[root@chensiqi1 ~]# sed -n '13,17p' /etc/ssh/sshd_config
Port 52113
PermitRootLogin no
PermitEmptyPasswords no
UseDNS no
GSSAPIAuthentication no
命令说明:查看增加的文本内容,选项-n与命令p的具体用法见后文的6.2.4查。
6.2.2 删
- 前面给大家演示了第一招,是不是很好用。如果没看懂,请再看一遍。如果学会了,那就跟着我来学习第2招--->删除指定行文本。
- 这个功能也是非常得有用,比如我们想删除文件中的某些行,以前最常用的是vi或vim命令,但现在我们知道了sed命令,就应该使用这个高逼格的命令完成任务了。
- 这里我们需要用到1个sed命令;
- “d”:删除文本,记忆方法:d的全拼是delete,意思是删除。
- 因为删除功能比较简单,因此我们结合地址范围一起说明。我们前面学过sed命令可以对一行文本为目标进行处理(在单行前后增加一行或多行文本),接下来我们看一下如何对多行文本为目标操作。
6.2.2.1指定执行的地址范围
sed软件可以对单行或多行文本进行处理。如果在sed命令前面不指定地址范围,那么默认会匹配所有行。
用法:n1[,n2]{sed -commands}
地址用逗号分隔开,n1,n2可以用数字,正则表达式,或者二者的组合表示。
地址范围 | 含义 |
---|---|
10{sed-commands} | 对第10行操作 |
10,20{sed-commands} | 对10到20行操作,包括第10,20行 |
10,+20{sed-commands} | 对10到30(10+20)行操作,包括第10,30行 |
1~2{sed-commands} | 对1,3,5,7.....行操作 |
10,${sed-commands} | 对10到最后一行($代表最后一行)操作,包括第10行 |
/chensiqi/{sed-commands} | 对匹配chensiqi的行操作 |
/chensiqi/,/Alex/{sed-commands} | 对匹配chensiqi的行到匹配Alex的行操作 |
/chensiqi/,${sed-commands} | 对匹配chensiqi的行到最后一行操作 |
/chensiqi/,10{sed-commands} | 对匹配chensiqi的行到第10行操作,注意:如果前10行没有匹配到chensiqi,sed软件会显示10行以后的匹配chensiqi的行 |
1,/Alex/{sed-commands} | 对第1行到匹配Alex的行操作 |
/chensiqi/,+2{sed-commands} | 对匹配chensiqi的行到其后的2行操作 |
下面用具体的例子演示一下,测试文件还是person.txt
[root@chensiqi1 ~]# sed 'd' person.txt
[root@chensiqi1 ~]#
命令说明:如果在sed命令前面不指定地址范围,那么默认会匹配所有行,然后使用d命令删除功能就会删除这个文件的所有内容
[root@chensiqi1 ~]# sed '2d' person.txt
101,chensiqi,CEO
103,Alex,COO
104,yy,CFO
105,feixue,CIO
命令说明:这个单行删除想必大家能理解,指定删除第2行的文本102,zhangyang,CTO
[root@chensiqi1 ~]# sed '2,5d' person.txt
101,chensiqi,CEO
命令说明:‘2,5d’指定删除第2行到第5行的内容,d代表删除操作。
ok,上面我们实验完了数字地址范围,接下来我们实验一下正则表达式的地址范围,虽然说可以使用正则表达式,但是我们还是习惯写出完整的匹配字符串,达到精确匹配的目的。
[root@chensiqi1 ~]# sed '/zhangyang/d' person.txt
101,chensiqi,CEO
103,Alex,COO
104,yy,CFO
105,feixue,CIO
命令说明:
在sed软件中,使用正则的格式和awk一样,使用2个”/“包含指定的正则表达式,即“/正则表达式/”。
当然也可以使用两个正则表达式,如下例所示。
[root@chensiqi1 ~]# sed '/chensiqi/,/Alex/d' person.txt
104,yy,CFO
105,feixue,CIO
命令说明:这是正则表达式形式的多行删除,也是以逗号分隔2个地址,最后结果是删除包含“chensiqi”的行到包含“Alex”的行
[root@chensiqi1 ~]# sed '3,$d' person.txt
101,chensiqi,CEO
102,zhangyang,CTO
命令说明:学过正则表达式后我们知道“$”代表行尾,但是在sed中就有一些变化了,“$”在sed中代表文件的最后一行。因此本例子的含义是删除第3行到最后一行的文本,包含第3行和最后一行,因此剩下第1,2行的内容。
接下来我们看一些特殊情况。
[root@chensiqi1 ~]# sed '/chensiqi/,3d' person.txt
104,yy,CFO
105,feixue,CIO
命令说明:这个例子是删除包含“chensiqi”的行到第3行的内容。但这种组合有一个比较特殊的情况,如果前3行之外还有这个“chensiqi”字眼,sed软件还是会找他“麻烦”,请看下面例子。
[root@chensiqi1 ~]# sed '$a 106,chensiqi,CMO' person.txt
101,chensiqi,CEO
102,zhangyang,CTO
103,Alex,COO
104,yy,CFO
105,feixue,CIO
106,chensiqi,CMO
命令说明:为了不造成同学们实验文本改来改去导致不同意,因此我用上面的命令语句只是临时修改内存数据,然后通过管道符号传给sed软件。
[root@chensiqi1 ~]# sed '$a 106,chensiqi,CMO' person.txt | sed '/chensiqi/,3d'
104,yy,CFO
105,feixue,CIO
命令说明:从命令结果我们可以看到,不仅是第1行(101,chensiqi,CEO)到第3行(103,ALex,COO)被删除了,而且最后一行(106,chensiqi,CMO)也被删除了。因此我们可以得出一个小结论,sed软件使用正则表达式会找出所有匹配的行,即使是有数字地址限制。
再来看一个特殊情况。
[root@chensiqi1 ~]# sed '2,/O/d' person.txt
101,chensiqi,CEO
104,yy,CFO
105,feixue,CIO
命令说明:
从第2行开始删除到含字母O的行结束,但是我们发现第3,4,5行都含有字母O,命令结果显示只删除了第2,3行,属于最短删除。这个怎么理解?
还是可以从上面命令执行流程图理解,从第2行开始循环,sed软件第一次遇到字母O(第三行)就认为循环结束了。
[root@chensiqi1 ~]# sed '2,/o/d' person.txt
101,chensiqi,CEO
命令说明:
从第2行开始删除,但是后面文本没有字母O,因此一直循环下去,直到文本结束,sed软件自动终止。
大家看完上面的例子是不是一脸懵逼,其实很简单。在工作中我们最常用的还是数字地址这种精确匹配方式,像上面的正则地址或混合地址这种模糊匹配用的比较少,了解即可。
6.2.2.2 特殊符号~(步长)解析
格式:“First~step”表示从开始,以步长step递增,这个在数学中叫做等差数列
例子:
- 1~2 匹配1,3,5,7.....#-->用于只输出奇书行,大伙仔细观察一下每个数字的差值。
- 2~2 匹配2,4,6,8....#-->用于只输出偶数行
- 1~3 匹配1,4,7,10.....
- 2~3 匹配2,5,8,11.....
[root@chensiqi1 ~]# seq 10
1
2
3
4
5
6
7
8
9
10
命令说明:seq命令能够生成从1到10的数字序列。
[root@chensiqi1 ~]# seq 10 | sed -n '1~2p'
1
3
5
7
9
命令说明:
上面的命令主要验证特殊符号“~”的效果,其他sed命令用法n和p请见后文详解,大家只需要知道这个命令可以将“1~2”指定的行显示出来即可。
上面例子测试了“1~2”的效果,大家也可以手动测试一下“2~2”,“1~3”,“2~3”,看一下他们的结果是不是符合等差数列。
补充小知识:
如果大家想生成奇数数列,其实上面的方法是为了举例,并不是一个很好的方法,因为seq命令自带这种功能。
[root@chensiqi1 ~]# seq 1 2 10
1
3
5
7
9
命令说明:seq命令格式seq起始值 公差 结束值
再来一例:
[root@chensiqi1 ~]# sed '1~2d' person.txt
102,zhangyang,CTO
104,yy,CFO
命令说明:“1~2”这是指定行数的另一种格式,从第1行开始以步长2递增的行(1,3,5),因此删掉第1,3,5行,即所有的奇数行。
6.2.2.3 特殊符号+解析
[root@chensiqi1 ~]# sed '1,+2d' person.txt
104,yy,CFO
105,feixue,CIO
命令说明:这其实是做个加法运算,‘1,+2d’==>删除第1行到第3(1+2)行的文本。
上面是特殊符号“+”的用法,大家知道即可。
6.2.2.4 特殊符号!解析
感叹号“!”我们在很多命令里都接触过,大部分都是取反的意思,在sed中也不例外。
[root@chensiqi1 ~]# sed '2,3!d' person.txt
102,zhangyang,CTO
103,Alex,COO
命令说明:在地址范围“2,3”后面加上“
!”,如果不加“!”表示删除第2行和第3行,结果如下面的例子所示,然后加上“!”的结果就是除了第2行和第3行以外的内容都删除,这个方法可以作为显示文件的第2,3行题目的补充方法。
[root@chensiqi1 ~]# sed '2,3d' person.txt
101,chensiqi,CEO
104,yy,CFO
105,feixue,CIO
企业案例2:打印文件内容但不包含chensiqi
这是一道企业面试题,很简单,就是把包含chensiqi字符串的行删除掉即可。我们可以用“grep -v”取反得到我们想要的结果,但是这里我们使用sed软件实现。
[root@chensiqi1 ~]# sed '/chensiqi/d' person.txt
102,zhangyang,CTO
103,Alex,COO
104,yy,CFO
105,feixue,CIO
命令说明:删除包含“chensiqi”的行,就直接用正则匹配字符串chensiqi即可
6.2.3 改
大家现在已经学完了sed软件2大招式,是不是很厉害!当然后面还有更厉害的--->改.在学习linux时,我们最常见的操作就是改配置文件,改参数等等,而且更妙的是前面我们学习过的增加和删除其实都可以用我们准备要学的修改变相实现。
6.2.3.1 按行替换
首先说一下按行替换,这个功能用的很少,所以大家了解即可。这里用到的sed命令是:
“c”:用新行取代旧行,记忆方法:c的全拼是change,意思是替换。
例子:
[root@chensiqi1 ~]# sed '2c 106,dandan,CSO' person.txt
101,chensiqi,CEO
106,dandan,CSO
103,Alex,COO
104,yy,CFO
105,feixue,CIO
命令说明:使用sed命令c将原来第2行“102,zhangyang,CTO”替换成“106,dandan,CSO”,整行替换
6.2.3.2 文本替换
- 接下来说的这个功能,有工作经验的同学应该非常的熟悉,因为使用sed软件80%的场景就是使用替换功能。
- 这里用到的sed命令,选项:
“s”:单独使用-->将每一行中第一处匹配的字符串进行替换==>sed命令
“g”:每一行进行全部替换-->sed命令s的替换标志之一(全局替换),非sed命令。
“-i”:修改文件内容-->sed软件的选项,注意和sed命令i区别。
sed软件替换模型
sed -i 's/目标内容/替换内容/g' chensiqi.log
sed -i 's#目标内容#替换内容#g'
观察特点
1,两边是引号,引号里面的两边分别为s和g,中间是三个一样的字符/或#作为定界符。字符#能在替换内容包含字符/有助于区别。定界符可以是任意字符如:或|等,但当替换内容包含定界符时,需要转义:或|.经过长期实践,建议大家使用#作为定界符。
2,定界符/或#,第一个和第二个之间的就是被替换的内容,第二个和第三个之间的就是替换后的内容。
3,s#目标内容#替换内容#g ,“目标内容”能用正则表达式,但替换内容不能用,必须是具体的。因为替换内容使用正则的话会让sed软件无所适从,它不知道你要替换什么内容。
4,默认sed软件是对模式空间(内存中的数据)操作,而-i选项会更改磁盘上的文件内容。
例子:
[root@chensiqi1 ~]# sed 's#zhangyang#dandan#g' person.txt
101,chensiqi,CEO
102,dandan,CTO
103,Alex,COO
104,yy,CFO
105,feixue,CIO
命令说明:将需要替换的文本“zhangyang”放在第一个和第二个“#”之间,将替换后的文本“dandan”放在第二个核第三个“#”之间。结果为第二行的“zhangyang”替换为“dandan”。
修改文件:
[root@chensiqi1 ~]# cat person.txt
101,chensiqi,CEO
102,zhangyang,CTO
103,Alex,COO
104,yy,CFO
105,feixue,CIO
命令说明:从上面命令的结果我们就知道sed命令默认不会修改文件的内容
[root@chensiqi1 ~]# sed -i 's#zhangyang#dandan#g' person.txt
命令说明:如果想真正的修改文件内容,我们就需要使用选项“-i”,这个要和sed命令“i”区分开来。同时我们可以发现命令执行后的结果是没有任何输出的。
[root@chensiqi1 ~]# cat person.txt
101,chensiqi,CEO
102,dandan,CTO
103,Alex,COO
104,yy,CFO
105,feixue,CIO
命令说明:当我们再次查看这个文件时,我们就发现这个文件已经被修改成功了。因此大家以后如果使用替换功能时,应该首先不用选项“-i”测试一下,确保操作无误,最后使用“-i”修改文件
还原测试文件:
[root@chensiqi1 ~]# sed -i 's#dandan#zhangyang#g' person.txt
命令说明:还原测试文件,这一步大家不要忘了执行,不然后面就跟不上步骤了
企业案例3:指定行修改配置文件
前面我们学过的模型能够将文件中所有满足条件的文本进行操作,但是我们也会碰到指定行精确修改配置文件的需求,因为这样可以避免修改多了地方。
[root@chensiqi1 ~]# sed '3s#0#9#' person.txt
101,chensiqi,CEO
102,zhangyang,CTO
193,Alex,COO
104,yy,CFO
105,feixue,CIO
命令说明:
前面学习的例子在sed命令“s”前没有指定地址范围,因此默认是对所有行进行操作。
而这个案例要求只将第3行的0换成9,这里就用到了我们前面学过的地址范围知识,在sed命令“s”前加上“3”就代表对第3行进行替换
6.2.3.3 变量替换
变量替换其实和前面的文本替换是一样的,就是具体的文本变成了变量,同时要求大家对引号的用法要有清晰的理解,因此对双引号和单引号的区别不太理解的同学,请往前面翻页到【引号的区别总结】再次复习一下。
[root@chensiqi1 ~]# cat >test.txt<<KOF #再新建一个简单的测试文本
> a
> b
> a
> KOF
[root@chensiqi1 ~]# cat test.txt
a
b
a
[root@chensiqi1 ~]# x=a #->设置变量x并 赋值a
[root@chensiqi1 ~]# y=b #-> 设置变量y并赋值b
[root@chensiqi1 ~]# echo $x
a
[root@chensiqi1 ~]# echo $x $y
a b
命令说明:打印变量x,y验证一下,需要使用$符号引用变量
不使用引号:
[root@chensiqi1 ~]# sed s#$x#$y#g test.txt
b
b
b
命令说明:使用变量进行替换,从执行结果中我们可以发现替换成功了,test.txt文件中所有的a都替换成了b。同时我们可以发现s#$x#$y#g没有使用引号,当然这种写法并不是特别标准。
[root@chensiqi1 ~]# sed 's#'$x'#'$y'#g' test.txt
b
b
b
命令说明:表面看起来单引号是可以用的,但其实这里用了障眼法,在你们眼中分段‘$x’和'$y',但其实分段是‘s#’和‘#’和‘#g’,所以$x和$y并没有被引号扩起来,和上面的例子就一样了。
使用eval命令:
[root@chensiqi1 ~]# eval sed 's#$x#$y#g' test.txt
b
b
b
命令说明:这里给大家扩展一个Linux内置命令eval,这个命令能读入变量,并将他们组合成一个新的命令,然后执行。首先eval会解析变量$x和变量$y,最后达到的效果和双引号是一样的。
扩展:最快速的获取IP地址的方法
[root@chensiqi1 ~]# hostname -I
192.168.197.133
6.2.3.4 分组替换()和\1的使用说明
sed软件的()的功能可以记住正则表达式的一部分,其中,\1为第一个记住的模式即第一个小括号中的匹配内容,\2第二个记住的模式,即第二个小括号中的匹配内容,sed最多可以记住9个。
例:echo "I am chensiqi teacher."如果想保留这一行的单词chensiqi,删除剩下部分,使用圆括号标记想保留的部分。
[root@chensiqi1 ~]# echo "I am chensiqi teacher." | sed 's#^.*am \([a-z]\+\) tea.*$#\1#g'
chensiqi
[root@chensiqi1 ~]# echo "I am chensiqi teacher." | sed -r 's#^.*am ([a-z]+) tea.*$#\1#g'
chensiqi
[root@chensiqi1 ~]# echo "I am chensiqi teacher." | sed -r 's#I (.*) (.*) teacher.#\1\2#g'
amchensiqi
命令说明:
sed如果不加-r后缀,那么默认不支持扩展正则表达式,需要\符号进行转义。小括号的作用是将括号里的匹配内容进行分组以便在第2和第3个#号之间进行sed的反向引用,\1代表引用第一组,\2代表引用第二组
再来看个题目:请执行命令取出linux中的eth0的IP地址?
[root@chensiqi1 ~]# ifconfig eth0 | sed -n '2p'
inet addr:192.168.197.133 Bcast:192.168.197.255 Mask:255.255.255.0
[root@chensiqi1 ~]# ifconfig eth0 | sed -n '2p' | sed -r 's#^.*addr:(.*) Bcast:.*$#\1#g'
192.168.197.133
也可以进行组合
[root@chensiqi1 ~]# ifconfig eth0 | sed -rn '2s#^.*addr:(.*) Bcast:.*$#\1#gp'
192.168.197.133
命令说明:
这道题是需要把ifconfig eth0执行结果的第2行的IP地址取出来,上面答案的思路是用IP地址来替换第2行的内容。
企业案例4:系统开机启动项优化(利用sed)
[root@chensiqi1 ~]# chkconfig --list | egrep -v "sshd|crond|rsyslog|sysstat|network" | awk '{print $1}' |sed -r 's#^(.*)#chkconfig \1 off#g' |bash
这题也可以应用awk直接一步到位
[root@chensiqi1 ~]# chkconfig --list | egrep -v "sshd|crond|network|rsyslog|sysstat" | awk '{print "chkconfig",$1,"off"}' | bash
6.2.3.5 特殊符号&代表被替换的内容
这是一个特殊技巧,在适合的场景使用特别方便。下面用特殊符号“&”与分组替换一起使用,进行对比。
[root@chensiqi1 ~]# sed -r 's#(.*),(.*),(.*)#& ----- \1 \2 \3#' person.txt
101,chensiqi,CEO ----- 101 chensiqi CEO
102,zhangyang,CTO ----- 102 zhangyang CTO
103,Alex,COO ----- 103 Alex COO
104,yy,CFO ----- 104 yy CFO
105,feixue,CIO ----- 105 feixue CIO
命令说明:
1,这里将分组替换和&符号放在一起对比
2,命令中的分组替换使用了3个小括号,每个小括号分别代表每一行以逗号作为分隔符的每一列。
3,上面命令的&符号代表每一行,即模型中‘s#目标内容#替换内容#g’的目标内容。
企业案例5:批量重命名文件
当前目录下有文件如下所示:
[root@chensiqi1 chen]# find ./ -name "*_finished.jpg"
./stu_102999_1_finished.jpg
./stu_102999_5_finished.jpg
./stu_102999_3_finished.jpg
./stu_102999_2_finished.jpg
./stu_102999_4_finished.jpg
要求用sed命令重命名,效果为
stu_102999_1_finished.jpg==>stu_102999_1.jpg,即删除文件名的_finished
解题思路:因为这是文件名,不能直接yongsed命令替换,因此还需要借助mv命令重命名,格式为:mv stu_102999_1_finished.jpg stu_102999_1.jpg.我们需要拼凑这样的格式,然后使用bash命令执行即可。
[root@chensiqi1 chen]# find ./ -name "*_finished.jpg" | sed -r 's#^(.*)_finished(.*)#\1\2#g'
./stu_102999_1.jpg
./stu_102999_5.jpg
./stu_102999_3.jpg
./stu_102999_2.jpg
[root@chensiqi1 chen]# find ./ -name "*_finished.jpg" | sed -r 's#^(.*)_finished(.*)#& \1\2#g'
./stu_102999_1_finished.jpg ./stu_102999_1.jpg
./stu_102999_5_finished.jpg ./stu_102999_5.jpg
./stu_102999_3_finished.jpg ./stu_102999_3.jpg
./stu_102999_2_finished.jpg ./stu_102999_2.jpg
./stu_102999_4_finished.jpg ./stu_102999_4.jpg
[root@chensiqi1 chen]# find ./ -name "*_finished.jpg" | sed -r 's#^(.*)_finished(.*)#mv & \1\2#g'
mv ./stu_102999_1_finished.jpg ./stu_102999_1.jpg
mv ./stu_102999_5_finished.jpg ./stu_102999_5.jpg
mv ./stu_102999_3_finished.jpg ./stu_102999_3.jpg
mv ./stu_102999_2_finished.jpg ./stu_102999_2.jpg
mv ./stu_102999_4_finished.jpg ./stu_102999_4.jpg
[root@chensiqi1 chen]# find ./ -name "*_finished.jpg" | sed -r 's#^(.*)_finished(.*)#mv & \1\2#g' |bash
[root@chensiqi1 chen]# ls
stu_102999_1.jpg stu_102999_2.jpg stu_102999_3.jpg stu_102999_4.jpg stu_102999_5.jpg
命令说明:
1.“\1”代表前面“(^.*)”匹配内容,“&”代表“s# #”里被替换的内容,这里匹配到的是完整的文件名。
2.使用bash命令执行,bash命令执行标准输入的语句,如同我们在命令行输入语句后敲回车。
6.2.4 查
- 学到这里,大家可以稍微喘口气了,因为sed里最常用最重要的我们已经学完了,接下来我们轻松的学完最后一招-->查看文本
- 这个功能也是非常得有用,比如我们想查看文件中的某些行,以前最常用的是cat或more或less命令等,但这些命令有些缺点,就是不能查看指定的行。而我们用了很久的sed命令就有了这个功能了。而且我们前面也说过使用sed比其他命令vim等读取速度更快!
- 这里我们需要用到1个sed命令
- “p”:输出指定内容,但默认会输出2次匹配的结果,因此使用-n选项取消默认输出,记忆方法:p的全拼是print,意思是打印。
6.2.4.1 按行查询
[root@chensiqi1 ~]# sed '2p' person.txt
101,chensiqi,CEO
102,zhangyang,CTO
102,zhangyang,CTO
103,Alex,COO
104,yy,CFO
105,feixue,CIO
[root@chensiqi1 ~]# sed -n '2p' person.txt
102,zhangyang,CTO
命令说明:选项-n取消默认输出,只输出匹配的文本,大家只需要记住使用命令p必用选项-n。
[root@chensiqi1 ~]# sed -n '2,3p' person.txt
102,zhangyang,CTO
103,Alex,COO
命令说明:查看文件的第2行到3行,使用地址范围“2,3”。取行就用sed,最简单
[root@chensiqi1 ~]# sed -n '1~2p' person.txt
101,chensiqi,CEO
103,Alex,COO
105,feixue,CIO
命令说明:打印文件的1,3,5行。~代表步长
[root@chensiqi1 ~]# sed -n 'p' person.txt
101,chensiqi,CEO
102,zhangyang,CTO
103,Alex,COO
104,yy,CFO
105,feixue,CIO
命令说明:不指定地址范围,默认打印全部内容。
6.2.4.2 按字符串查询
[root@chensiqi1 ~]# sed -n '/CTO/p' person.txt
102,zhangyang,CTO
命令说明:打印含CTO的行
[root@chensiqi1 ~]# sed -n '/CTO/,/CFO/p' person.txt
102,zhangyang,CTO
103,Alex,COO
104,yy,CFO
命令说明:打印含CTO的行到含CFO的行。
6.2.4.3 混合查询
[root@chensiqi1 ~]# sed -n '2,/CFO/p' person.txt
102,zhangyang,CTO
103,Alex,COO
104,yy,CFO
命令说明:打印第2行到含CFO的行。
[root@chensiqi1 ~]# sed -n '/feixue/,2p' person.txt
105,feixue,CIO
命令说明:特殊情况,前两行没有匹配到feixue,就向后匹配,如果匹配到feixue就打印此行。所以这种混合地址不推荐使用。
6.2.4.4 过滤多个字符
[root@chensiqi1 ~]# sed -rn '/chensiqi|yy/p' person.txt
101,chensiqi,CEO
104,yy,CFO
命令说明:
使用扩展正则“|”,为了不使用转义符号“\”,因此使用-r选项开启扩展正则表达式模式
7,sed命令应用知识扩展
7.1 sed修改文件的同时进行备份
[root@chensiqi1 ~]# cat person.txt
101,chensiqi,CEO
102,zhangyang,CTO
103,Alex,COO
104,yy,CFO
105,feixue,CIO
[root@chensiqi1 ~]# sed -i.bak 's#zhangyang#NB#g' person.txt
[root@chensiqi1 ~]# cat person.txt
101,chensiqi,CEO
102,NB,CTO
103,Alex,COO
104,yy,CFO
105,feixue,CIO
[root@chensiqi1 ~]# cat person.txt.bak
101,chensiqi,CEO
102,zhangyang,CTO
103,Alex,COO
104,yy,CFO
105,feixue,CIO
命令行说明:
在-i参数的后边加上.bak(.任意字符),sed会对文件进行先备份后修改
7.2 特殊符号=获取行号
[root@chensiqi1 ~]# sed '=' person.txt
1
101,chensiqi,CEO
2
102,NB,CTO
3
103,Alex,COO
4
104,yy,CFO
5
105,feixue,CIO
命令说明:使用特殊符号“=”就可以获取文件的行号,这是特殊用法,记住即可。从上面的命令结果我们也发现了一个不好的地方:行号和行不在一行。
[root@chensiqi1 ~]# sed '1,3=' person.txt
1
101,chensiqi,CEO
2
102,NB,CTO
3
103,Alex,COO
104,yy,CFO
105,feixue,CIO
命令说明:只打印1,2,3行的行号,同时打印输出文件中的内容
[root@chensiqi1 ~]# sed '/yy/=' person.txt
101,chensiqi,CEO
102,NB,CTO
103,Alex,COO
4
104,yy,CFO
105,feixue,CIO
命令说明:
只打印正则匹配行的行号,同时输出文件中的内容
[root@chensiqi1 ~]# sed -n '/yy/=' person.txt
4
命令说明:只显示行号但不显示行的内容即取消默认输出。
[root@chensiqi1 ~]# sed -n '$=' person.txt
5
命令说明:
“$”代表最后一行,因此显示最后一行的行号,变相得出文件的总行数。
方法改进:
[root@chensiqi1 ~]# sed '=' person.txt | sed 'N;s#\n# #'
1 101,chensiqi,CEO
2 102,NB,CTO
3 103,Alex,COO
4 104,yy,CFO
5 105,feixue,CIO
命令说明:前面sed获取文件的行号有一个缺点,我们这里使用Sed命令N来补偿这个缺点。Sed命令N读取下一行数据并附加到模式空间。
7.3 sed如何取不连续的行
[root@chensiqi1 ~]# sed -n '1p;3p;5p' person.txt
101,chensiqi,CEO
103,Alex,COO
105,feixue,CIO
7.4 特殊符号{}的使用
[root@chensiqi1 ~]# sed -n '2,4p;=' person.txt
1
102,NB,CTO
2
103,Alex,COO
3
104,yy,CFO
4
5
命令说明:-n去掉默认输出,2,4p,输出2到4行内容,=输出全部的行的行号
[root@chensiqi1 ~]# sed -n '2,4{p;=}' person.txt
102,NB,CTO
2
103,Alex,COO
3
104,yy,CFO
4
命令说明:
‘2,4{p;=}’代表统一输出2,4行的行号和内容
Linux三剑客之awk精讲(基础与进阶)
标签(空格分隔): Linux实战教学笔记-陈思齐
快捷跳转目录:
第1章 awk基础入门
要弄懂awk程序,必须熟悉了解这个工具的规则。本实战笔记的目的是通过实际案例或面试题带同学们熟练掌握awk在企业中的用法,而不是awk程序的帮助手册。
1.1 awk简介
- 一种名字怪异的语言
- 模式扫描和处理
awk不仅仅时linux系统中的一个命令,而且是一种编程语言,可以用来处理数据和生成报告(excel)。处理的数据可以是一个或多个文件,可以是来自标准输入,也可以通过管道获取标准输入,awk可以在命令行上直接编辑命令进行操作,也可以编写成awk程序来进行更为复杂的运用。本章主要讲解awk命令的运用。
1.2 学完awk你可以掌握:
- 记录与字段
- 模式匹配:模式与动作
- 基本的awk执行过程
- awk常用内置变量(预定义变量)
- awk数组(工作常用)
- awk语法:循环,条件
- awk常用函数
- 向awk传递参数
- awk引用shell变量
- awk小程序及调试思路
1.3 awk环境简介
[root@chensiqi1 ~]# cat /etc/redhat-release
CentOS release 6.8 (Final)
[root@chensiqi1 ~]# uname -r
2.6.32-642.el6.x86_64
[root@chensiqi1 ~]# ll `which awk`
lrwxrwxrwx. 1 root root 4 Dec 23 20:25 /bin/awk -> gawk
[root@chensiqi1 ~]# awk --version
GNU Awk 3.1.7
Copyright (C) 1989, 1991-2009 Free Software Foundation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/.
1.4 awk的格式
- awk指令是由模式,动作,或者模式和动作的组合组成。
- 模式既pattern,可以类似理解成sed的模式匹配,可以由表达式组成,也可以是两个正斜杠之间的正则表达式。比如NR==1,这就是模式,可以把他理解为一个条件。
- 动作即action,是由在大括号里面的一条或多条语句组成,语句之间使用分号隔开。比如awk使用格式:
awk处理的内容可以来自标准输入(<),一个或多个文本文件或管道。
- pattern既模式,也可以理解为条件,也叫找谁,你找谁?高矮,胖瘦,男女?都是条件,既模式。
- action既动作,可以理解为干啥,找到人之后你要做什么。
模式和动作的详细介绍我们放在后面部分,现在大家先对awk结构有一个了解。
1.5 模式动作
示例1-1: 基本的模式和动作
[root@chensiqi1 ~]# awk -F ":" 'NR>=2 && NR<=6{print NR,$1}' /etc/passwd
2 bin
3 daemon
4 adm
5 lp
6 sync
命令说明:
-F 指定分隔符为冒号,相当于以“:”为菜刀,进行字段的切割。
NR>=2 && NR<=6:这部分表示模式,是一个条件,表示取第2行到第6行。
{print NR,$1}:这部分表示动作,表示要输出NR行号和$1第一列。
示例1-2 只有模式
[root@chensiqi1 ~]# awk -F ":" 'NR>=2&&NR<=6' /etc/passwd
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
命令说明:
-F指定分隔符为冒号
NR>=2&&NR<=6这部分是条件,表示取第2行到第6行。
但是这里没有动作,这里大家需要了解如果只有条件(模式)没有动作,awk默认输出整行
示例1-3:只有动作
[root@chensiqi1 ~]# awk -F ":" '{print NR,$1}' /etc/passwd
1 root
2 bin
3 daemon
4 adm
5 lp
6 sync
7 shutdown
8 halt
9 mail
10 uucp
以下省略....
命令说明:
-F指定分隔符为冒号
这里没有条件,表示对每一行都处理
{print NR,$1}表示动作,显示NR行号与$1第一列
这里要理解没有条件的时候,awk会处理每一行。
示例1-4:多个模式和动作
[root@chensiqi1 ~]# awk -F ":" 'NR==1{print NR,$1}NR==2{print NR,$NF}' /etc/passwd
1 root
2 /sbin/nologin
命令说明:
-F指定分隔符为冒号
这里有多个条件与动作的组合
NR==1表示条件,行号(NR)等于1的条件满足的时候,执行{print NR,$1}动作,输出行号与第一列。
NR==2表示条件,行号(NR)等于2的条件满足的时候,执行{print NR,$NF}动作,输出行号与最后一列($NF)
注意:
- Pattern和{Action}需要用单引号引起来,防止shell作解释。
- Pattern是可选的。如果不指定,awk将处理输入文件中的所有记录。如果指定一个模式,awk则只处理匹配指定的模式的记录。
- {Action}为awk命令,可以是但个命令,也可以多个命令。整个Action(包括里面的所有命令)都必须放在{ 和 }之间。
- Action必须被{ }包裹,没有被{ }包裹的就是Patern
- file要处理的目标文件
1.6 awk执行过程
在深入了解awk前,我们需要知道awk如何处理文件的。
示例1-5 示例文件的创建
[root@chensiqi1 ~]# mkdir /server/files/ -p
[root@chensiqi1 ~]# head /etc/passwd > /server/files/awkfile.txt
[root@chensiqi1 ~]# cat /server/files/awkfile.txt
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin
这个文件仅包含十行文件,我们使用下面的命令:
示例1-6 awk执行过程演示
[root@chensiqi1 ~]# awk 'NR>=2{print $0}' /server/files/awkfile.txt
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin
命令说明:
条件NR>=2,表示行号大于等于2时候,执行{print $0}显示整行。
awk是通过一行一行的处理文件,这条命令中包含模式部分(条件)和动作部分(动作),awk将处理模式(条件)指定的行
1.6.1 小结awk执行过程
1)awk读入第一行内容
2)判断是否符合模式中的条件NR>=2
a,如果匹配则执行对应的动作{print $0}
b,如果不匹配条件,继续读取下一行
3)继续读取下一行
4)重复过程1-3,直到读取到最后一行(EOF:end of file)
1.7 记录和字段
接下来我给大家带来两个新概念记录和字段,这里为了方便大家理解可以把记录就当作行即记录==行,字段相当于列,字段==列。
名称 | 含义 |
---|---|
record | 记录,行 |
field | 域,区域,字段,列 |
1.7.1 记录(行)
查看一下下面这段文字
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin
思考:
一共有多少行呢?你如何知道的?通过什么标志?
awk对每个要处理的输入数据认为都是具有格式和结构的,而不仅仅是一堆字符串。默认情况下,每一行内容都是一条记录,并以换行符分隔(\n)结束
1.7.2 记录分隔符-RS
- awk默认情况下每一行都是一个记录(record)
- RS既record separator输入输出数据记录分隔符,每一行是怎么没的,表示每个记录输入的时候的分隔符,既行与行之间如何分隔。
- NR既number of record 记录(行)号,表示当前正在处理的记录(行)的号码。
- ORS既output record separator 输出记录分隔符。
awk使用内置变量RS来存放输入记录分隔符,RS表示的是输入的记录分隔符,这个值可以通过BEGIN模块重新定义修改。
示例1-1:使用“/”为默认记录分隔符
示例文件:
[root@chensiqi1 ~]# cat /server/files/awkfile.txt
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin
[root@chensiqi1 ~]# awk 'BEGIN{RS="/"}{print NR,$0}' /server/files/awkfile.txt
1 root:x:0:0:root:
2 root:
3 bin
4 bash
bin:x:1:1:bin:
5 bin:
6 sbin
7 nologin
daemon:x:2:2:daemon:
8 sbin:
9 sbin
10 nologin
adm:x:3:4:adm:
11 var
12 adm:
13 sbin
14 nologin
lp:x:4:7:lp:
15 var
16 spool
17 lpd:
18 sbin
19 nologin
sync:x:5:0:sync:
20 sbin:
21 bin
22 sync
shutdown:x:6:0:shutdown:
23 sbin:
24 sbin
25 shutdown
halt:x:7:0:halt:
26 sbin:
27 sbin
28 halt
mail:x:8:12:mail:
29 var
30 spool
31 mail:
32 sbin
33 nologin
uucp:x:10:14:uucp:
34 var
35 spool
36 uucp:
37 sbin
38 nologin
命令说明:
在每行的开始先打印输出NR(记录号行号),并打印出每一行$0(整行)的内容。
我们设置RS(记录分隔符)的值为“/”,表示一行(记录)以“/”结束
在awk眼中,文件是从头到尾一段连续的字符串,恰巧中间有些\n(回车换行符),\n也是字符哦。
我们回顾下“行(记录)”到底是什么意思?
- 行(记录):默认以\n(回车换行)结束。而这个行的结束不就是记录分隔符嘛。
- 所以在awk中,RS(记录分隔符)变量表示着行的结束符号(默认是回车换行)
在工作中,我们可以通过修改RS变量的值来决定行的结束标志,最终来决定“每行”的内容。
为了方便人们理解,awk默认就把RS的值设置为“\n”
注意:
awk的BEGIN模块,我会在后面(模式-BEGIN模块)详细讲解,此处大家仅需要知道在BEGIN模块里面我们来定义一些awk内置变量即可。
1.7.3 对$0的认识
- 如1.7.2的例子,可以看出awk中$0表示整行,其实awk使用$0来表示整条记录。记录分隔符存在RS变量中,或者说每个记录以RS内置变量结束。
- 另外,awk对每一行的记录号都有一个内置变量NR来保存,每处理完一条记录,NR的值就会自动+1
- 下面通过示例来加深印象。
示例1-2:NR记录号
[root@chensiqi1 ~]# awk '{print NR,$0}' /server/files/awkfile.txt
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6 sync:x:5:0:sync:/sbin:/bin/sync
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
8 halt:x:7:0:halt:/sbin:/sbin/halt
9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
10 uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin
命令说明:
NR既number of record,当前记录的记录号,刚开始学也可以理解为行号。
$0表示整行或者说整个记录
1.7.4 企业面试题:按单词出现频率降序排序(计算文件中每个单词的重复数量)
注:(此处使用sort与uniq即可)
题目:
题目创建方法:
sed -r '1,10s#[^a-zA-Z]+# #g' /etc/passwd>/server/files/count.txt
[root@chensiqi1 files]# cat /server/files/count.txt
root x root root bin bash
bin x bin bin sbin nologin
daemon x daemon sbin sbin nologin
adm x adm var adm sbin nologin
lp x lp var spool lpd sbin nologin
sync x sync sbin bin sync
shutdown x shutdown sbin sbin shutdown
halt x halt sbin sbin halt
mail x mail var spool mail sbin nologin
uucp x uucp var spool uucp sbin nologin
思路:
让所有单词排成一列,这样每个单词都是单独的一行
1)设置RS值为空格
2)将文件里面的所有空格替换为回车换行符“\n”
3)grep所有连续的字母,grep -o参数让他们排成一列。
方法一:
[root@chensiqi1 files]# awk 'BEGIN{RS="[ ]+"}{print $0}' count.txt | sort |uniq -c|sort
1
1 bash
1 lpd
2 daemon
2 lp
3 adm
3 halt
3 mail
3 root
3 shutdown
3 spool
3 sync
3 uucp
4 var
5 bin
6 nologin
10 x
12 sbin
方法二:
[root@chensiqi1 files]# cat count.txt | tr " " "\n" | sort | uniq -c | sort
1 bash
1 lpd
2 daemon
2 lp
3 adm
3 halt
3 mail
3 root
3 shutdown
3 spool
3 sync
3 uucp
4 var
5 bin
6 nologin
10 x
12 sbin
方法三:
[root@chensiqi1 files]# grep -o "[a-zA-Z]\+" count.txt | sort | uniq -c | sort
1 bash
1 lpd
2 daemon
2 lp
3 adm
3 halt
3 mail
3 root
3 shutdown
3 spool
3 sync
3 uucp
4 var
5 bin
6 nologin
10 x
12 sbin
1.7.5 awk记录知识小结
- NR存放着每个记录的号(行号)读取新行时候会自动+1
- RS是输入数据的记录的分隔符,简单理解就是可以指定每个记录的结尾标志。
- RS作用就是表示一个记录的结束
- 当我们修改了RS的值,最好配合NR(行)来查看变化,也就是修改了RS的值通过NR查看结果,调试awk程序。
- ORS输出数据的记录的分隔符
awk学习技巧一则:
大象放冰箱分几步?打开冰箱,把大象放进去,关闭冰箱门。
awk也是一样的,一步一步来,先修改了RS,然后用NR调试,看看到底如何分隔的。然后通过sort排序,uniq -c去重
1.7.6 字段(列)
- 每条记录都是由多个区域(field)组成的,默认情况下区域之间的分隔符是由空格(即空格或制表符)来分隔,并且将分隔符记录在内置变量FS中,每行记录的区域数保存在awk的内置变量NF中。
- FS既field separator,输入字段(列)分隔符。分隔符就是菜刀,把一行字符串切为很多个区域。
- NF既number of fileds,表示一行中列(字段)的个数,可以理解为菜刀切过一行后,切成了多少份。
OFS输出字段(列)分隔符
- awk使用内置变量FS来记录区域分隔符的内容,FS可以在命令行上通过-F参数来更改,也可以通过BEGIN模块来更改。
- 然后通过$n,n是整数,来取被切割后的区域,$1取第一个区域,$2取第二个区域,$NF取最后一个区域。
下面我们通过示例来加强学习。
示例1-3:指定分隔符
[root@chensiqi1 files]# awk -F ":" 'NR>=2&&NR<=5{print $1,$3}' /server/files/awkfile.txt
bin 1
daemon 2
adm 3
lp 4
命令说明:
以:(冒号)为分隔符,显示第2行到第5行之间的第一区域和第三区域。
- 此处的FS知识一个字符,其实它可以指定多个的,此时FS指定的值可以是一个正则表达式。
- 正常情况下,当你指定分隔符(非空格)的时候,例如指定多个区域分隔符,每个分隔符就是一把刀,把左右两边切为两个部分。
企业面试题:同时取出chensiqi和215379068这两个内容(指定多分隔符)
[root@chensiqi1 files]# echo "I am chensiqi,my qq is 1234567890">>/server/files/chensiqi.txt
[root@chensiqi1 files]# cat /server/files/chensiqi.txt
I am chensiqi,my qq is 1234567890
同时取出chensiqi和1234567890这两个内容。
思路:
我们用默认的想法一次使用一把刀,需要配合管道的。如何同时使用两把刀呢?看下面的结果
[root@chensiqi1 files]# awk -F "[ ,]" '{print $3,$NF}' /server/files/chensiqi.txt
chensiqi 1234567890
命令说明:
通过命令-F参数指定区域分隔符
[ ,]是正则表达式里面的内容,它表示一个整体,“一个”字符,既空格或者逗号(,),合并在一起,-F “[ ,]”就表示以空格或者逗号(,)为区域分隔符
小技巧:
在动作(‘{print $3,$NF}’)里面的逗号,表示空格,其实动作中的逗号就是OFS的值,我们会在后面说明。刚开始大家把动作中的都逗号,当作空格即可。
示例:默认分隔符和指定分隔符会有些差异
[root@chensiqi1 files]# ifconfig eth0 | awk 'NR==2' >/server/files/awkblank.txt
[root@chensiqi1 files]# cat /server/files/awkblank.txt
inet addr:192.168.197.133 Bcast:192.168.197.255 Mask:255.255.255.0
#默认分隔符时候
[root@chensiqi1 files]# awk '{print $1}' /server/files/awkblank.txt
inet
#指定分隔符时候
[root@chensiqi1 files]# awk -F "[ :]+" '{print $1}' /server/files/awkblank.txt
[root@chensiqi1 files]# awk -F "[ :]+" '{print $2}' /server/files/awkblank.txt
inet
命令说明:
awk默认的FS分隔符对于空格序列,一个空格或多个空格tab都认为是一样的,一个整体。
- 这个文件的开头有很多连续的空格,然后才是inet这个字符
- 当我们使用默认的分隔符的时候,$1是有内容的。
- 当我们指定其他分隔符(非空格)时候,区域会有所变化
- 到底为何会这样,我们在这里不再深入研究,只要了解有这种情况,注意一下即可。
1.7.7 ORS与OFS简介
现在说说ORS和OFS这两个内置变量的含义。
- RS是输入记录分隔符,决定awk如何读取或分隔每行(记录)
- ORS表示输出记录分隔符,决定awk如何输出一行(记录)的,默认是回车换行(\n)
- FS是输入区域分隔符,决定awk读入一行后如何再分为多个区域。
- OFS表示输出区域分隔符,决定awk输出每个区域的时候使用什么分隔她们。
- awk无比强大,你可以通过RS,FS决定awk如何读取数据。你也可以通过修改ORS,OFS的值指定awk如何输出数据。
1.8 字段与记录小结
现在你应该会对awk的记录字段有所了解了,下面我们总结一下,学会给阶段性知识总结是学好运维的必备技能。
- RS记录分隔符,表示每行的结束标志
- NR行号(记录号)
- FS字段分隔符,每列的分隔标志或结束标志
- NF就是每行有多少列,每个记录中字段的数量
$符号表示取某个列(字段),$1$2$NF
- NF表示记录中的区域(列)数量,
$NF
取最后一个列(区域。) - FS(-F)字段(列)分隔符,-F(FS)“:”<==>‘BEGIN{FS=':'}’
- RS 记录分隔符(行的结束标识)
- NR 行号
- 选好合适的刀FS(***),RS,OFS,ORS
- 分隔符==>结束标识
- 记录与区域,你就对我们所谓的行与列,有了新的认识(RS,FS)
1.9 awk基础入门总结
到了这里我们回头看看,我们之前学习的内容。
- awk的命令行结构
- awk的模式和动作
- awk的记录和字段
比较核心常用的是字段。
另外这些企业面试题可是学会awk的必备,必须自己也能写出来。
第2章 awk进阶
2.1 awk模式与动作
接下来就详细介绍下,awk的模式都有几种:
- 正则表达式作为模式
- 比较表达式作为模式
- 范围模式
- 特殊模式BEGIN和END
awk的模式是你玩好awk的必备也是最基础的内容,必须熟练掌握
2.2 正则表达式作为模式
awk同sed一样也可以通过模式匹配来对输入的文本进行匹配处理。说到模式匹配,肯定少不了正则表达式,awk也支持大量的正则表达式模式,大部分与sed支持的元字符类似,而且正则表达式是玩转三剑客的必备工具,下表列出了awk支持的正则表达式元字符:
awk默认就支持的元字符:
元字符 | 功能 | 示例 | 解释 |
---|---|---|---|
^ | 字符串开头 | /^chensiqi/或$3~/^chensiqi/ | 匹配所有以chensiqi开头的字符串;匹配出所有第三列中以chensiqi开头的 |
$ | 字符串结尾 | /chensiqi$/或$3~/chensiqi$/ | 匹配所有以chensiqi结尾的字符串;匹配第三列中以chensiqi结尾的 |
.(点) | 匹配任意但个字符(包括回车符) | /c..l/ | 匹配字母c,然后两个任意字符,再以l结尾的行 |
* | 重复0个或多个前一个字符 | /a*cool/ | 匹配0个或多个a之后紧跟着cool的行 |
+ | 重复前一个字符一次或多次 | /a+b/ | 匹配一个或多个a加上字符串b的行 |
? | 匹配0个或一个前边的字符 | /a?b/ | 匹配以字母a或b或c开头的行 |
[] | 匹配指定字符组内的任一个字符 | /^[abc]/ | 匹配以字母a或b或c开头的行 |
[^] | 匹配不在指定字符组内的任一字符 | /^[^abc]/ | 匹配不以字母a或b或c开头的行 |
() | 子表达式组合 | /(chensiqi)+/ | 表示一个或多个cool组合,当有一些字符需要组合时,使用括号括起来 |
| |
或者的意思 | /(chensiqi)|B/ |
匹配chensiqi或字母B的行 |
awk默认不支持的元字符:(参数--posix)
元字符 | 功能 | 示例 | 解释 |
---|---|---|---|
x{m} | x字符重复m次 | /cool{5}/ | 匹配l字符5次 |
x{m,} | x字符重复至少m次 | /(cool){2,}/ | 匹配cool整体,至少2次 |
x{m,n} | x字符重复至少m次,但不超过n次 | /(cool){5,6}/ | 匹配cool整体,至少5次,最多6次 |
提示:
- 加括号代表整体匹配,不加那么就匹配前边的一个字符。awk默认不支持这种形式的正则,需要加--posix参数或者--re-interval
- 正则表达式的运用,默认是在行内查找匹配的字符串,若有匹配则执行action操作,但是有时候仅需要固定的列来匹配指定的正则表达式,比如:我想取/etc/passwd文件中第五列{$5}这一列查找匹配mail字符串的行,这样就需要用另外两个匹配操作符,并且awk里面只有这两个操作符来匹配正则表达式。
2.2.1 awk正则匹配操作符
awk正则匹配操作符:
|~|用于对记录或区域的表达式进行匹配|
|--|--|
|!~|用于表达与~相反的意思|
下面还是通过具体示例来看看,awk如何来通过正则表达式匹配字符串的
2.2.2 awk正则表达式匹配整行
[root@chensiqi1 files]# awk -F ":" '/^root/' awkfile.txt
root:x:0:0:root:/root:/bin/bash
和下面的效果是一样的
[root@chensiqi1 files]# awk -F ":" '$0~/^root/' awkfile.txt
root:x:0:0:root:/root:/bin/bash
提示:
awk只用正则表达式的时候是默认匹配整行的即‘$0~/^root/’和‘/^root/’是一样的。
2.2.3 awk正则表达式匹配一行中的某一列
[root@chensiqi1 files]# awk -F ":" '$5~/shutdown/' awkfile.txt
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
提示:
- $5表示第五个区域(列)
- ~表示匹配(正则表达式匹配)
- /shutdown/表示匹配shutdown这个字符串
合并在一起
$5~/shutdown/表示第五个区域(列)匹配正则表达式/shutdown/,既第5列包含shutdown这个字符串,则显示这一行。
2.2.4 某个区域中的开头和结尾
知道了如何使用正则表达式匹配操作符之后,我们来看看awk正则与grep和sed不同的地方。
awk正则表达式
|^|匹配一个字符串的开头|
|--|--|
|$|匹配一个字符串的结尾|
在sed和grep这两个命令中,我们都把它们当作行的开头和结尾。但是在awk中他表示的是字符串的开头和结尾。
接下来我们通过练习题来联系awk如何使用正则表达式。
2.2.5 创建测试环境
[root@chensiqi1 ~]# cat >>/server/files/reg.txt<<KOF
Zhang Dandan 41117397 :250:100:175
Zhang Xiaoyu 390320151 :155:90:201
Meng Feixue 80042789 :250:60:50
Wu Waiwai 70271111 :250:80:75
Liu Bingbing 41117483 :250:100:175
Wang Xiaoai 3515064655 :50:95:135
Zi Gege 1986787350 :250:168:200
Li Youjiu 918391635 :175:75:300
Lao Nanhai 918391635 :250:100:175
KOF
2.2.6 测试文件说明
Zhang Dandan 41117397 :250:100:175
Zhang Xiaoyu 390320151 :155:90:201
Meng Feixue 80042789 :250:60:50
Wu Waiwai 70271111 :250:80:75
Liu Bingbing 41117483 :250:100:175
Wang Xiaoai 3515064655 :50:95:135
Zi Gege 1986787350 :250:168:200
Li Youjiu 918391635 :175:75:300
Lao Nanhai 918391635 :250:100:175
说明:
- 第一列是姓氏
- 第二列是名字
- 第一列第二列合起来就是姓名
- 第三列是对应的ID号码
- 最后三列是三次捐款数量
2.2.7 awk正则表达式练习题
练习题1:显示姓Zhang的人的第二次捐款金额及她的名字
练习题2:显示Xiaoyu的名字和ID号码
练习题3:显示所有以41开头的ID号码的人的全名和ID号码
练习题4:显示所有以一个D或X开头的人名全名
练习题5:显示所有ID号码最后一位数字是1或5的人的全名
练习题6:显示Xiaoyu的捐款,每个值都有以$开头。如$520$200$135
练习题7:显示所有人的全名,以姓,名的格式显示,如Meng,Feixue
2.2.8 awk正则表达式练习题-详解
示例1:显示姓Zhang的人的第二次捐款金额及她的名字
[root@chensiqi1 files]# cat reg.txt
Zhang Dandan 41117397 :250:100:175
Zhang Xiaoyu 390320151 :155:90:201
Meng Feixue 80042789 :250:60:50
Wu Waiwai 70271111 :250:80:75
Liu Bingbing 41117483 :250:100:175
Wang Xiaoai 3515064655 :50:95:135
Zi Gege 1986787350 :250:168:200
Li Youjiu 918391635 :175:75:300
Lao Nanhai 918391635 :250:100:175
[root@chensiqi1 files]# awk -F "[ :]+" '$1~/^Zhang/{print $2,$(NF-1)}' reg.txt
Zhang 100
Zhang 90
说明:
- -F指定分隔符,现在大家知道了-F即FS也是支持正则表达式的。
- 【 :】+ 表示连续的空格或冒号
- -F “【 :】+” 以连续的空格或冒号为分隔符
- /Zhang/表示条件,整行中包含Dan字符的这个条件。
- {print $1,$(NF-1)} 表示动作,满足条件后,执行显示第一列($1)和倒数第二列($(NF-1))当然$5也可以。
注意:
NF是一行中有多少列,NF-1整行就是倒数第二列。
$(NF-1)就是取倒数第二列内容。
示例2:显示Xiaoyu的姓氏和ID号码
[root@chensiqi1 files]#cat reg.txt
Zhang Dandan 41117397 :250:100:175
Zhang Xiaoyu 390320151 :155:90:201
Meng Feixue 80042789 :250:60:50
Wu Waiwai 70271111 :250:80:75
Liu Bingbing 41117483 :250:100:175
Wang Xiaoai 3515064655 :50:95:135
Zi Gege 1986787350 :250:168:200
Li Youjiu 918391635 :175:75:300
Lao Nanhai 918391635 :250:100:175
[root@chensiqi1 files]# awk -F "[ :]+" '$2~/^Xiaoyu/{print $1,$3}' reg.txt
Zhang 390320151
命令说明:
指定分隔符-F “【:】+”
$2~/Xiaoyu/表示条件,第二列包含Xiaoyu时候执行对应的动作
{print $1,$3}表示动作,显示第一列和第三列的内容
示例3:显示所有以41开头的ID号码的人的全名和ID号码
[root@chensiqi1 files]# cat reg.txt
Zhang Dandan 41117397 :250:100:175
Zhang Xiaoyu 390320151 :155:90:201
Meng Feixue 80042789 :250:60:50
Wu Waiwai 70271111 :250:80:75
Liu Bingbing 41117483 :250:100:175
Wang Xiaoai 3515064655 :50:95:135
Zi Gege 1986787350 :250:168:200
Li Youjiu 918391635 :175:75:300
Lao Nanhai 918391635 :250:100:175
[root@chensiqi1 files]# awk -F "[ :]+" '$3~/^(41)/{print $1,$2,$3}' reg.txt
Zhang Dandan 41117397
Liu Bingbing 41117483
示例4:显示所有以一个D或X开头的人名全名
[root@chensiqi1 files]# cat reg.txt
Zhang Dandan 41117397 :250:100:175
Zhang Xiaoyu 390320151 :155:90:201
Meng Feixue 80042789 :250:60:50
Wu Waiwai 70271111 :250:80:75
Liu Bingbing 41117483 :250:100:175
Wang Xiaoai 3515064655 :50:95:135
Zi Gege 1986787350 :250:168:200
Li Youjiu 918391635 :175:75:300
Lao Nanhai 918391635 :250:100:175
[root@chensiqi1 files]# awk -F "[ :]+" '$2~/^D|^X/{print $1,$2}' reg.txt
Zhang Dandan
Zhang Xiaoyu
Wang Xiaoai
命令说明:
-F “【 :】+”指定分隔符
|表示或,^以...开头
注意:
这里要用()括号表示即^(D|X)相当于^D|^X,有的同学写成^D|X这样是错误的。
示例5:显示所有ID号码最后一位数字是1或5的人的全名
[root@chensiqi1 files]# cat reg.txt
Zhang Dandan 41117397 :250:100:175
Zhang Xiaoyu 390320151 :155:90:201
Meng Feixue 80042789 :250:60:50
Wu Waiwai 70271111 :250:80:75
Liu Bingbing 41117483 :250:100:175
Wang Xiaoai 3515064655 :50:95:135
Zi Gege 1986787350 :250:168:200
Li Youjiu 918391635 :175:75:300
Lao Nanhai 918391635 :250:100:175
[root@chensiqi1 files]# awk -F "[ :]+" '$3~/1$|5$/{print $1,$2}' reg.txt
Zhang Xiaoyu
Wu Waiwai
Wang Xiaoai
Li Youjiu
Lao Nanhai
示例6:显示Xiaoyu的捐款,每个值都有以$开头。如$520$200$135
[root@chensiqi1 files]# cat reg.txt
Zhang Dandan 41117397 :250:100:175
Zhang Xiaoyu 390320151 :155:90:201
Meng Feixue 80042789 :250:60:50
Wu Waiwai 70271111 :250:80:75
Liu Bingbing 41117483 :250:100:175
Wang Xiaoai 3515064655 :50:95:135
Zi Gege 1986787350 :250:168:200
Li Youjiu 918391635 :175:75:300
Lao Nanhai 918391635 :250:100:175
[root@chensiqi1 files]# awk -F "[ :]+" '$2~/Xiaoyu/{print "$"$4"$"$5"$"$6}' reg.txt
$155$90$201
示例7:显示所有人的全名,以姓,名的格式显示,如Meng,Feixue
[root@chensiqi1 files]# cat reg.txt
Zhang Dandan 41117397 :250:100:175
Zhang Xiaoyu 390320151 :155:90:201
Meng Feixue 80042789 :250:60:50
Wu Waiwai 70271111 :250:80:75
Liu Bingbing 41117483 :250:100:175
Wang Xiaoai 3515064655 :50:95:135
Zi Gege 1986787350 :250:168:200
Li Youjiu 918391635 :175:75:300
Lao Nanhai 918391635 :250:100:175
[root@chensiqi1 files]# awk -F "[ ]+" '{print $1","$2}' reg.txt
Zhang,Dandan
Zhang,Xiaoyu
Meng,Feixue
Wu,Waiwai
Liu,Bingbing
Wang,Xiaoai
Zi,Gege
Li,Youjiu
Lao,Nanhai
2.2.9 企业面试题:取出网卡eth0的ip地址
最简单:hostname -I
awk处理:
方法一:
[root@chensiqi1 files]# ifconfig eth0|awk 'BEGIN{RS="[ :]"}NR==31'
192.168.197.133
方法二:
[root@chensiqi1 files]# ifconfig eth0 | awk -F "(addr:)|( Bcast:)" 'NR==2{print $2}'
192.168.197.133
方法三:
[root@chensiqi1 files]# ifconfig eth0 | awk -F "[ :]+" 'NR==2{print $4}'
192.168.197.133
方法四:
[root@chensiqi1 files]# ifconfig eth0 | awk -F "[^0-9.]+" 'NR==2{print $2}'
192.168.197.133
提示:
- 前边的三种方法都还是比较好理解的,这第四种方法,需要学会逆向思维,看看我们要的结果10.0.0.50,ip地址:数字和点(.),我是否可以指定分隔符,以数字和点以外的字符为分隔符呢?
- 换句话说就是要排除数字和点(.)正则表达式与排除常用的就是[^0-9.]即不匹配数字和点(.)
- 最后-F “[^0-9]”位分隔符,但是要使用+,表示连续的。合起来就是:awk -F “[^0-9.]+” 'NR==2{print $2}'
注意:
正则表达式是玩好awk的必要条件,必会掌握
2.2.10 明明白白扩展正则表达式:+(加号)
[root@chensiqi1 files]# echo "------======1########2"
------======1########2
[root@chensiqi1 files]# echo "------======1########2" | grep "[-=#]"
------======1########2
[root@chensiqi1 files]# echo "------======1########2" | grep -o "[-=#]"
-
-
-
-
-
-
=
=
=
=
=
=
#
#
#
#
#
#
#
#
2.2.11 awk正则之{} -花括号
awk中的花括号有些不常用,但是偶尔会用到这里简单介绍。
示例:取出awkfile中第一列包含一个o或者两个o的行
[root@chensiqi1 files]# awk -F: '$1~/o{1,2}/' awkfile.txt
[root@chensiqi1 files]# awk -F: --posix '$1~/o{1,2}/' awkfile.txt
root:x:0:0:root:/root:/bin/bash
daemon:x:2:2:daemon:/sbin:/sbin/nologin
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
[root@chensiqi1 files]# awk -F: --re-interval '$1~/o{1,2}/' awkfile.txt
root:x:0:0:root:/root:/bin/bash
daemon:x:2:2:daemon:/sbin:/sbin/nologin
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
2.2.12 企业案例1:取出常用服务端口号
思路:
linux下面服务与端口信息的对应表格在/etc/services里面,所以这道题要处理/etc/services文件。
我们简单分析以下servics文件:
[root@chensiqi1 ~]# sed -n '23,30p' /etc/services
tcpmux 1/tcp # TCP port service multiplexer
tcpmux 1/udp # TCP port service multiplexer
rje 5/tcp # Remote Job Entry
rje 5/udp # Remote Job Entry
echo 7/tcp
echo 7/udp
discard 9/tcp sink null
discard 9/udp sink null
从23行开始基本上每一行第一列是服务名称,第二列的第一部分是端口号,第二列的第二部分是tcp或udp协议。
方法:
[root@chensiqi1 ~]# awk -F "[ /]+" '$1~/^(ssh)$|^(http)$|^(https)$|^(mysql)$|^(ftp)$/{print $1,$2}' /etc/services |sort|uniq
ftp 21
http 80
https 443
mysql 3306
ssh 22
提示:
- |是或者的意思,正则表达式
- sort是将输出结果排序
- uniq是去重复但不标记重复个数
- uniq -c去重复但标记重复个数
2.2.13 企业案例2:取出常用服务端名称
同学们自己尝试下
2.3 比较表达式做为模式-需要一些例子
之前我们看了正则表达式在awk下的运用,下面再具体看看比较表达式如何在awk下工作。
awk是一种编程语言,能够进行更为复杂的判断,当条件为真时候,awk就执行相关的action。主要是针对某一区域做出相关的判断,比如打印成绩在80分以上的行,这样就必须对这一区域做比较判断,下表列出了awk可以使用的关系运算符,可以用来比较数字字符串,还有正则表达式。当表达式为真时候,表达式结果1,否0,只有表达式为真,awk才执行相关的action
运算符 | 含义 | 示例 |
---|---|---|
< | 小于 | x>y |
<= | 小于等于 | x<=y |
== | 等于 | x==y |
!= | 不等于 | x!=y |
>= | 大于或等于 | x>=y |
> | 大于 | x<y |
以上运算符是针对数字的,下面两个运算符之前已有示例,针对字符串
~ | 与正则表达式匹配 | x~/y/ |
---|---|---|
!~ | 与正则表达式不匹配 | x!~y |
2.3.1 企业面试题:取出文件/etc/services的23~30行
思路:
想表示一个范围,一个行的范围,就要用到NR这个内置变量了,同时也要用到比较表达式。
答案:
[root@www ~]# awk 'NR>=23&&NR<=30' /etc/services
[root@www ~]# awk 'NR>22&&NR<31' /etc/services
过程:
[root@www ~]# awk 'NR>=23&&NR<=30' /etc/services
tcpmux 1/tcp # TCP port service multiplexer
tcpmux 1/udp # TCP port service multiplexer
rje 5/tcp # Remote Job Entry
rje 5/udp # Remote Job Entry
echo 7/tcp
echo 7/udp
discard 9/tcp sink null
discard 9/udp sink null
[root@www ~]# awk 'NR>22&&NR<31' /etc/services
tcpmux 1/tcp # TCP port service multiplexer
tcpmux 1/udp # TCP port service multiplexer
rje 5/tcp # Remote Job Entry
rje 5/udp # Remote Job Entry
echo 7/tcp
echo 7/udp
discard 9/tcp sink null
discard 9/udp sink null
说明:
1)比较表达式比较常用的还是表示大于等于,小于等于或者等于,根据这个例子来学习即可
2)NR表示行号,大于等于23即,NR>=23小于等于30,即NR<=30
3)合起来就是NR>=23并且NR<=30,&&表示并且,同时成立的意思。
4)换一种表达式方法就是大于22行小于31行,即NR>22&&NR<31
2.3.2 如果判断某一列是否等于某个字符呢?
示例:找出/etc/passwd中第五列是root的行
测试文件:
[root@www ~]# cat /server/files/awk_equal.txt
root:x:0:0:root:/root:/bin/bash
root:x:0:0:rootroot:/root:/bin/bash
root:x:0:0:rootrooot:/root:/bin/bash
root:x:0:0:rootrooot:/root:/bin/bash
root:x:0:0:/root:/bin/bash
答案:
awk -F":" '$5=="root"' /server/files/awk_equal.txt
awk -F":" '$5~/^root$/' /server/files/awk_equal.txt
过程:
#方法一:
[root@www ~]# awk -F":" '$5=="root"' /server/files/awk_equal.txt
root:x:0:0:root:/root:/bin/bash
#方法二:
[root@www ~]# awk -F":" '$5~/^root$/' /server/files/awk_equal.txt
root:x:0:0:root:/root:/bin/bash
我们如果想要完全匹配root这个字符串,那就用$5=="root"
即可,这也是答案里面给大家的。
方法二:
此题也可通过正则匹配来限制root的字符串。$5~/^root$/
2.4 范围模式
pattern1 | pattern2 | |
---|---|---|
从哪里来 | 到 | 哪里去 |
条件1 | 条件2 |
- 范围模式简单理解就是从哪里来,到哪里去。
- 匹配从条件1开始到条件2介绍的范围
1)还记得sed使用地址范围来处理文本内容嘛?awk的范围模式,与sed类似,但是又有不同,awk不能直接使用行号来作为范围起始地址,因为awk具有内置变量NR来存储记录号,所有需要使用NR=1,NR=5这样来使用。
2)范围模式处理的原则是:先匹配从第一个模式的首次出现到第二个模式的首次出现之间的内容,执行action。然后匹配从第一个模式的下一次出现到第二个模式的下一次出现,直到文本结束。如果匹配到第一个模式而没有匹配到第二个模式,则awk处理从第一个模式开始直到文本结束全部的行。如果第一个模式不匹配,就算第二个模式匹配,awk依旧不处理任何行。
awk '/start pos/,/end pos/{print $)} passwd chensiqi'
awk '/start pos/,NR==XXX{print $0}' passwd chensiqi
范围模式的时候,范围条件的时候,表达式必须能匹配一行。
示例1:
[root@www files]# awk 'NR==2,NR==5{print NR,$0}' count.txt
2 bin x bin bin sbin nologin
3 daemon x daemon sbin sbin nologin
4 adm x adm var adm sbin nologin
5 lp x lp var spool lpd sbin nologin
说明:
条件是:从第二行,到第五行
动作是:显示行号(NR)和整行($0)
合起来就是显示第二行到第五行的行好和整行的内容
示例2:
[root@www files]# awk '/^bin/,NR==5{print NR,$0}' awkfile.txt
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
说明:
条件是:从以bin开头的行,到第五行
动作是:显示行号和整行内容
合起来就是显示从以bin开头的行,到第五行中的行号和整行内容。
示例3:
[root@www files]# awk -F":" '$5~/^bin/,/^lp/{print NR,$0}' awkfile.txt
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
说明:
条件:从第五列以bin开头的行到以lp开头的行
动作:显示行号和正航内容
合起来:从第三列以bin开始的行到以lp开头的行并显示其行号和整行内容
[root@www files]# awk -F: '$5~/^bin/,$5~/^lp/{print NR,$0}' awkfile.txt
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
说明:
条件:从第三列以bin开头字符串的行到第三列以lp开头字符串的行
动作:显示行号和整行
2.5 awk特殊模式-BEGIN模式与END模式
- BEGIN模块再awk读取文件之前就执行,一般用来定义我们的内置变量(预定义变量,eg:FS,RS),可以输出表头(类似excel表格名称)
- BEGIN模式之前我们有在示例中提到,自定义变量,给内容变量赋值等,都使用过。需要注意的是BEGIN模式后面要接跟一个action操作块,包含在大括号内。awk必须在输入文件进行任何处理前先执行BEGIN里的动作(action)。我们可以不要任何输入文件,就可以对BEGIN模块进行测试,因为awk需要先执行完BEGIN模式,才对输入文件做处理。BEGIN模式常常被用来修改内置变量ORS,RS,FS,OFS等值。
2.5.1 BEGIN模块
1)第一个作用,内置变量的定义
示例:取eth0的IP地址
答案:
[root@www files]# ifconfig eth0|awk -F "(addr:)|( Bcast:)" 'NR==2{print $2}'
192.168.197.133
[root@www files]# ifconfig eth0 | awk -F "[ :]+" 'NR==2{print $4}'
192.168.197.133
[root@www files]# ifconfig eth0 | awk -F "[^0-9.]+" 'NR==2{print $2}'
192.168.197.133
#上面的也可以写成
[root@www files]# ifconfig eth0 | awk 'BEGIN{FS="(addr:)|( Bcast:)"} NR==2{print $2}'
192.168.197.133
[root@www files]# ifconfig eth0 | awk 'BEGIN{FS="[ :]+"}NR==2{print $4}'
192.168.197.133
[root@www files]# ifconfig eth0 | awk 'BEGIN{FS="[^0-9.]+"}NR==2{print $2}'
192.168.197.133
注意:
命令行-F本质就是修改的FS变量
2)第二个作用,在读取文件之前,输出些提示性信息(表头)。
[root@www files]# awk -F: 'BEGIN{print "username","UID"}{print $1,$3}' awkfile.txt
username UID #这就是输出的表头信息
root 0
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
halt 7
mail 8
uucp 10
说明:
要在第一行输出一些username和UID,我们应该想到BEGIN{}这个特殊的条件(模式),因为BEGIN{}在awk读取文件之前执行的。
所以结果是BEGIN{print "username","UID"}
,注意print命令里面双引号吃啥吐啥,原样输出。
然后我们实现了在输出文件内容之前输出“username”和“UID”,下一步输出文件的第一列和第三列即{print $1,$3}
最后结果就是BEGIN{print "username","UID"}{print $1,$3}
3)第三个作用,使用BEGIN模块的特殊性质,进行一些测试。
[root@www files]#简单输出内容:
[root@www files]# awk 'BEGIN{print "hello world!"}'
hello world!
[root@www files]# #进行计算
[root@www files]# awk 'BEGIN{print 10/3}'
3.33333
[root@www files]# awk 'BEGIN{print 10/3+1}'
4.33333
[root@www files]# awk 'BEGIN{print 10/3+1/4*9}'
5.58333
[root@www files]# #和变量有关的操作
[root@www files]# awk 'BEGIN{a=1;b=2;print a,b}'
1 2
[root@www files]# awk 'BEGIN{a=1;b=2;print a,b,a+b}'
1 2 3
4)第四种用法:配合getline读取文件,后面awk函数处讲解
2.5.2 awk中变量的概念简介
- 直接定义,直接使用即可
- awk中字母会被认为是变量,如果真的要给一个变量赋值字母(字符串),请使用双引号
[root@chensiqi files]# awk 'BEGIN{a=abcd;print a}'
[root@chensiqi files]# awk 'BEGIN{abcd=123456;a=abcd;print a}'
123456
[root@chensiqi files]# awk 'BEGIN{a="abcd";print a}'
abcd
说明:
没有文件awk依旧可以处理BEGIN模式下的动作(命令)
2.5.3 END模块
EHD在awk读取完所有的文件的时候,再执行END模块,一般用来输出一个结果(累加,数组结果),也可以是和BEGIN模块类似的结尾标识信息
[root@chensiqi files]# awk 'BEGIN{print "hello world!"}{print NR,$0}END{print "end of file"}' count.txt
hello world!
1 root x root root bin bash
2 bin x bin bin sbin nologin
3 daemon x daemon sbin sbin nologin
4 adm x adm var adm sbin nologin
5 lp x lp var spool lpd sbin nologin
6 sync x sync sbin bin sync
7 shutdown x shutdown sbin sbin shutdown
8 halt x halt sbin sbin halt
9 mail x mail var spool mail sbin nologin
10 uucp x uucp var spool uucp sbin nologin
end of file
与BEGIN模式相对应的END模式,格式一样,但是END模式仅在awk处理完所有输入行后才进行处理。
企业案例:统计/etc/servies文件里的空行数量
思路:
a)空行通过正则表达式来实现:^$
b)统计数量:
- grep -c
- awk
方法一:grep
[root@chensiqi files]# grep "^$" /etc/services | wc -l
16
[root@chensiqi files]# grep -c "^$" /etc/services
16
说明:
grep命令-c表示count计数统计包含^$的行一共有多少。
方法二:
[root@chensiqi files]# awk '/^$/{i++}END{print i}' /etc/services
16
提示:
使用了awk的技术功能,很常用
第一步:统计空行个数/^$/
表示条件,匹配出空行,然后执行{i++}(i++等于i=i+1)即:/^$/{i=i+1}
我们可以通过/^$/{i=i+1;print i}
来查看awk执行过程
[root@chensiqi files]# awk '/^$/{i=i+1;print "the value of i is:"i}' /etc/services
the value of i is:1
the value of i is:2
the value of i is:3
the value of i is:4
the value of i is:5
the value of i is:6
the value of i is:7
the value of i is:8
the value of i is:9
the value of i is:10
the value of i is:11
the value of i is:12
the value of i is:13
the value of i is:14
the value of i is:15
the value of i is:16
第二步:输出最后结果
- 但是我们只想要最后的结果16,不想要过程怎么办?使用END模式输出结果
- 因为END模式的特殊性质所以很适合输出最终结果
所以最终结果就是awk '/^$/{i=i+1}END{print "blank lines count:"i}' /etc/services
awk编程思想:
- 先处理,最后再END模块输出
{print NR,$0}body
模块处理,处理完毕后END{print "end of file"}
输出一个结果
企业面试题5:文件count.txt,文件内容是1到100(由seq 100生成),请计算文件每行值加起来的结果(计算1+...+100)
思路:
文件每一行都有且只有一个数字,所以我们要让文件的每行内容相加。
回顾一下上一道题我们用的是i++即i=i+1
这里我们需要使用到第二个常用的表达式
i=i+$0
对比一下,其实只是把上边的1换成了$0
[root@chensiqi files]# awk '{i=i+$0}END{print i}' count.txt
5050
2.6 awk中的动作
在一个模式-动作语句中,模式决定动作什么时候执行,有时候动作会非常简单:一条单独的打印或赋值语句。在有些时候,动作有可能是多条语句,语句之间用换行符或分号分开。
awk的动作中如果有两个或两个以上的语句,需要用分号分隔
动作部分大家理解为花括号里面的内容即可,总体分为:
- 表达式
- 流程控制语句
- 空语句
- 数组(以后如果有时间的话会再写一个awk高级部分进行介绍)
2.7 awk模式与动作小结
- awk命令核心由模式和动作组成
- 模式就是条件,动作就是具体干什么
1)正则表达式:必须掌握正则,熟练
2)条件表达式:比大小,比较是否相等
3)范围表达式:从哪里来到哪里去 - 注意BEGIN或END模块只能有一个。BEGIN{}BEGIN{}或者END{}END{}都是错误的。
2.8 总结awk执行过程
回顾一下awk的结构
awk -F 指定分隔符 ‘BRGIN{}END{}’,如下图
#awk完整执行过程
[root@chensiqi ~]# awk -F ":" 'BEGIN{RS="/";print "hello world!"}{print NR,$0}END{print "end of file"}' /server/files/awkfile.txt
hello world!
1 root:x:0:0:root:
2 root:
3 bin
4 bash
bin:x:1:1:bin:
5 bin:
6 sbin
7 nologin
daemon:x:2:2:daemon:
8 sbin:
9 sbin
10 nologin
adm:x:3:4:adm:
11 var
12 adm:
13 sbin
14 nologin
lp:x:4:7:lp:
15 var
16 spool
17 lpd:
18 sbin
19 nologin
sync:x:5:0:sync:
20 sbin:
21 bin
22 sync
shutdown:x:6:0:shutdown:
23 sbin:
24 sbin
25 shutdown
halt:x:7:0:halt:
26 sbin:
27 sbin
28 halt
mail:x:8:12:mail:
29 var
30 spool
31 mail:
32 sbin
33 nologin
uucp:x:10:14:uucp:
34 var
35 spool
36 uucp:
37 sbin
38 nologin
end of file
说明:
我们·同时再命令行定义了分隔符和在BEGIN模式中定义了RS内置变量,在最后通过END模式输出了结果
2.9 awk数组
awk提供了数组来存放一组相关的值。
awk是一种编程语言,肯定也支持数组的运用,但是又不同于c语言的数组。数组在awk中被称为关联数组,因为它的下标既可以是数字也可以是字符串。下标通常被称作key,并且与对应的数组元素的值关联。数组元素的key和值都存储在awk程序内部的一张表中,通过一定散列算法来存储,所以数组元素都不是按顺序存储的。打印出来的顺序也肯定不是按照一定的顺序,但是我们可以通过管道来对所需的数据再次操作来达到自己的效果。
如图不难发现,awk数组就和酒店一样。数组的名称就像是酒店名称,数组元素名称就像酒店房间号码,每个数组元素里面的内容就像是酒店房间里面的人。
2.10 图片-数组
假设我们有一个酒店
酒店<===>chensiqihotel
酒店里面有几个房间110,119,120,114这几个房间
酒店110房间<===>chensiqihotel[110]
酒店120房间<===>chensiqihotel[120]
酒店119房间<===>chensiqihotel[119]
酒店114房间<===>chensiqihotel[114]
酒店房间里面入住客人
酒店110房间住着xiaoyu<===>chensiqihotel[110]="xiaoyu"
酒店119房间住着ruxue<===>chensiqihotel[119]="ruxue"
酒店120房间住着dandan<===>chensiqihotel[120]="dandan"
酒店114房间住着waiwai<===>chensiqihotel[114]="waiwai"
示例:
[root@chensiqi ~]# awk 'BEGIN{chensiqihotel[110]="xiaoyu";chensiqihotel[119]="ruxue";chensiqihotel[120]="dandan";chensiqihotel[114]="waiwai";print chensiqihotel[110],chensiqihotel[119],chensiqihotel[120],chensiqihotel[114]}'
xiaoyu ruxue dandan waiwai
[root@chensiqi ~]# awk 'BEGIN{chensiqihotel[110]="xiaoyu";chensiqihotel[119]="ruxue";chensiqihotel[120]="dandan";chensiqihotel[114]="waiwai";for(hotel in chensiqihotel)print hotel,chensiqihotel[hotel]}'
110 xiaoyu
120 dandan
114 waiwai
119 ruxue
企业面试题1:统计域名访问次数
处理以下文件内容,将域名取出并根据域名进行计数排序处理:(百度和sohu面试题)
http://www.etiantian.org/index.html
http://www.etiantian.org/1.html
http://post.etiantian.org/index.html
http://mp3.etiantian.org/index.html
http://www.etiantian.org/3.html
http://post.etiantian.org/2.html
思路:
1)以斜线为菜刀取出第二列(域名)
2)创建一个数组
3)把第二列(域名)作为数组的下标
4)通过类似于i++的形式进行计数
5)统计后把结果输出
过程演示:
第一步:查看一下内容
[root@chensiqi ~]# awk -F "[/]+" '{print $2}' file
www.etiantian.org
www.etiantian.org
post.etiantian.org
mp3.etiantian.org
www.etiantian.org
post.etiantian.org
命令说明:
这是我们需要计数的内容
第二步:计数
[root@chensiqi ~]# awk -F "[/]+" '{i++;print $2,i}' file
www.etiantian.org 1
www.etiantian.org 2
post.etiantian.org 3
mp3.etiantian.org 4
www.etiantian.org 5
post.etiantian.org 6
命令说明:
i++:i最开始是空的,当awk读取一行,i自身+1
第三步:用数组替换i
[root@chensiqi ~]# awk -F "[/]+" '{h[$2]++;print $2,h["www.etiantian.org"]}' file
www.etiantian.org 1
www.etiantian.org 2
post.etiantian.org 2
mp3.etiantian.org 2
www.etiantian.org 3
post.etiantian.org 3
命令说明:
1)将i替换成h[$2];相当于我创建了一个数组h[],然后用$2作为我的房间号。但是目前房间里是没有东西的。也就是说h[$2]=h["www.etiantian.org"] and h["post.etiantian.org"] and h["mp3.etiantian.org"] 但是具体房间里是没有东西的也就是空。
2)h[$2]++就等于i++:也就是说我开始给房间里加东西;当出现同样的东西,我就++
3)print h["www.etiantian.org"]:意思就是说我开始要输出了。我要输出的是房间号为“www.etiantian.org”里面的内容。这里面的内容最早是空的,随着awk读取每一行一旦出现房间号为“www.etiantian.org”的房间时,我就给房间里的内容进行++。
4)综上,输出的结果中,每次出现www.etiantian.org时,h["www.etiantian.org"]就会++。因此最后的输出数字是3
第四步:输出最终计数结果
[root@chensiqi ~]# awk -F "[/]+" '{h[$2]++}END{for(i in h)print i,h[i]}' file
mp3.etiantian.org 1
post.etiantian.org 2
www.etiantian.org 3
[root@chensiqi ~]#
命令说明:
我们最终需要输出的是去重复以后的统计结果,所以得在END模块里进行输出
for(i in h)遍历这个数组,i里存的都是房间号
print i,h[i]:输出每一个房间号及其房间里的内容(计数结果)
提示:
awk的应用里最重要的一个功能就是计数,而数组在awk里最大的作用就是去重复。请同学们仔细理解,多动手试验一下。