三剑客介绍
grep:基于正则表达式查找满足条件的行;
awk:根据定位到的数据行处理其中的分段;
sed:根据定位到的数据行修改数据;
三剑客用途
grep:数据查找定位,查找或匹配文本
awk:数据切片,格式化文本,对文本进行较复杂格式处理
sed:数据修改,编辑匹配到的文本
类比SQL
grep=select * from table
awk=select field from table
sed=update table set field=new where field=old
grep参数
grep pattern file 匹配行内容
grep -i pattern file 忽略大小写
grep -v pattern file 不显示匹配的行
grep -o pattern file 把每个匹配的内容用独立的行显示
grep -E pattern file 使用扩展正则表达式
grep -A -B -C pattern file 打印命中数据的上下文
grep pattern -r dir/ 递归搜索
pattern正则表达式
- 基本表达式(BRE)
- ^ 开头 $ 结尾
- [a-z] [0-9] 区间
- *0个或多个
- 基本正则(BRE)与扩展正则的区别(ERE)
- ?非贪婪匹配
- +一个或者多个
- () 分组
- {} 范围约束
- | 匹配多个表达式的任何一个
awk基础
awk的基本语法如下
awk [options] ‘Pattern{Action}’ file
从字面上理解 ,action指的就是动作,awk擅长文本格式化,并且将格式化以后的文本输出,所以awk最常用的动作就是print和printf,因为awk要把格式化完成后的文本输出啊,所以,这两个动作最常用。
我们先从最简单用法开始了解awk,我们先不使用[options] ,也不指定pattern,直接使用最简单的action,从而开始认识awk,示例如下
上图中,我们只是使用awk执行了一个打印的动作,将testd文件中的内容打印了出来。
上图中的示例没有使用到options和pattern,上图中的awk '{print $5}' ,
表示输出df的信息的第5列,$5表示将当前行按照分隔符分割后的第5列,不指定分隔符时,默认使用空格作为分隔符,细心的你一定发现了,上述信息用的空格不止有一个,而是有连续多个空格,awk自动将连续的空格理解为一个分割符了,是不是比cut命令要简单很多,这样比较简单的例子,有利于我们开始了解awk。
awk是逐行处理的,逐行处理的意思就是说,当awk处理一个文本时,会一行一行进行处理,处理完当前行,再处理下一行,awk默认以”换行符”为标记,识别每一行,也就是说,awk跟我们人类一样,每次遇到”回车换行”,就认为是当前行的结束,新的一行的开始,awk会按照用户指定的分割符去分割当前行,如果没有指定分割符,默认使用空格作为分隔符。
NF表示当前行分割后的最后一列(NF均为内置变量)
注意,NF表示最后一个字段,NF表示当前行被分隔符切开以后,一共有几个字段。
也就是说,假如一行文本被空格分成了7段,那么NF的值就是7,7, 而(NF-1)。
我们也可以一次输出多列,使用逗号隔开要输出的多个列,如下,一次性输出第一列和第二列
同理,也可以一次性输出多个指定的列,如下图
我们发现,第一行并没有第5列,所以并没有输出任何文本,而第二行有第五列,所以输出了。
除了输出文本中的列,我们还能够添加自己的字段,将自己的字段与文件中的列结合起来,如下做法,都是可以的。
从上述实验中可以看出,awk可以灵活的将我们指定的字符与每一列进行拼接,或者把指定的字符当做一个新列插入到原来的列中,也就是awk格式化文本能力的体现。
但是要注意,1会被当做文本输出,示例如下
我们也可以输出整行,比如,如下两种写法都表示输出整行。
我们说过,awk的语法如下
awk [options] ‘Pattern{Action}’ file
而且我们说过awk是逐行处理的, 刚才已经说过了最常用的Action:print
现在,我们来认识下一Pattern,也就是我们所说的模式
不过,我们准备先把awk中最特殊的模式展示给大家,以后再介绍普通的模式,因为普通模式需要的篇幅比较长,所以我们先来总结特殊模式。
AWK 包含两种特殊的模式:BEGIN 和 END。
BEGIN 模式指定了处理文本之前需要执行的操作:
END 模式指定了处理完所有行之后所需要执行的操作:
什么意思呢?光说不练不容易理解,我们来看一些小例子,先从BEGIN模式开始,示例如下
上述写法表示,在开始处理test文件中的文本之前,先执行打印动作,输出的内容为”aaa”,”bbb”.
也就是说,上述示例中,虽然指定了test文件作为输入源,但是在开始处理test文本之前,需要先执行BEGIN模式指定的”打印”操作
既然还没有开始逐行处理test文件中的文本,那么是不是根本就不需要指定test文件呢,我们来试试。
经过实验发现,还真是,我们并没有给定任何输入来源,awk就直接输出信息了,因为,BEGIN模式表示,在处理指定的文本之前,需要先执行BEGIN模式中指定的动作,而上述示例没有给定任何输入源,但是awk还是会先执行BEGIN模式指定的”打印”动作,打印完成后,发现并没有文本可以处理,于是就只完成了”打印 aaa bbb”的操作。
这个时候,如果我们想要awk先执行BEGIN模式指定的动作,再根据执我们自定义的动作去操作文本,该怎么办呢?示例如下
上图中先打印出了”aaa bbb”,当BEGIN模式对应的动作完成后,在使用后面的动作处理对应的文本,即打印test文件中的第一列与第二列,这样解释应该比较清楚了吧。
看完上述示例,似乎更加容易理解BEGIN模式是什么意思了,BEGIN模式的作用就是,在开始逐行处理文本之前,先执行BEGIN模式所指定的动作。以此类推,END模式的作用就一目了然了,举例如下。
END模式就是在处理完所有的指定的文本之后,需要指定的动作。
那么,我们可以结合BEGIN模式和END模式一起使用。示例如下
上述示例中返回的结果有没有很像一张”报表”,有”表头” 、”表内容”、 “表尾”。
awk分隔符
awk有哪些分隔符,awk的默认分割符是空格,但是,这样描述并不精确,因为,awk的分隔符还分为两种,”输入分隔符” 和 “输出分隔符” 。
-
输入分隔符,英文原文为field separator,此处简称为FS
输入分割符,默认是空白字符(即空格),awk默认以空白字符为分隔符对每一行进行分割。
-
输出分割符,英文原文为output field separator,此处简称为OFS
awk将每行分割后,输出在屏幕上的时候,以什么字符作为分隔符,awk默认的输出分割符也是空格。
输入分隔符
输入分隔符比较容易理解,当awk逐行处理文本的时候,以输入分隔符为准,将文本切成多个片段,默认使用空格,但是,如果一段文字中没有空格,我们可以指定以特定的文字或符号作为输入分割符,比如下图中的例子,我们指定使用”#”作为输入分隔符。
上图中,我们使用了-F 选项,指定了使用#号作为输入分隔符,于是,awk将每一行都通过#号为我们分割了。
除了使用 -F 选项指定输入分隔符,还能够通过设置内部变量的方式,指定awk的输入分隔符,awk内置变量FS可以用于指定输入分隔符,但是在使用变量时,需要使用-v选项,用于指定对应的变量,比如 -v FS=’#’,如下图
其实不管是通过-F选项,还是通过FS这个内置变量,目的都是设置指定的输入分隔符,达到的效果是相同的。
而此处,我们使用了awk中的一个选项,就是-F
我们说过,awk的语法如下
awk [options] ‘Pattern{Action}’ file
而-F,就是options的一种,用于指定输入分隔符。
-v也是options的一种,用于设置变量的值。
我们已经将options 、pattern 、action都简单的应用了一遍,好了,我们已经”会用”awk了。
输出分隔符
当awk为我们输出每一列的时候,会使用空格隔开每一列,其实,这个空格,就是awk的默认的输出分隔符。
输出分割符的意思就是:当我们要对处理完的文本进行输出的时候,以什么文本或符号作为分隔符。
我们可以使用awk的内置变量OFS来设定awk的输出分隔符,当然,使用变量的时候要配合使用-v选项,示例如下
现在,我们可以同时指定输入分隔符和输出分割符了,示例如下
我们刚才解释了awk的输出分隔符,如果,在输出的时候,我们想要让两列合并在一起显示,不使用输出分隔符分开显示,该怎么做呢?如下图所示,蓝线之上使用默认的输出分隔符进行了分隔,而蓝线之下的两种方法均未使用输出分隔符进行分隔,而是将两列合在一起显示了。
awk ‘{print 2}’ 表示每行分割后,将第一列(第一个字段)和第二列(第二个字段)连接在一起输出。
awk ‘{print 2}’ 表示每行分割后,将第一列(第一个字段)和第二列(第二个字段)以输出分隔符隔开后显示。
awk变量
对于awk来说”变量”又分为”内置变量” 和 “自定义变量” , “输入分隔符FS”和”输出分隔符OFS”都属于内置变量。
内置变量就是awk预定义好的、内置在awk内部的变量,而自定义变量就是用户定义的变量。
我们先看看awk常用的一些内置变量,此处先大致列出其概念,只看概念并不容易理解其意思,不懂没关系,等到示例时你自然会明白。
awk常用的内置变量以及其作用如下
FS:输入字段分隔符, 默认为空白字符
OFS:输出字段分隔符, 默认为空白字符
RS:输入记录分隔符(输入换行符), 指定输入时的换行符
ORS:输出记录分隔符(输出换行符),输出时用指定符号代替换行符
NF:number of Field,当前行的字段的个数(即当前行被分割成了几列),字段数量
NR:行号,当前处理的文本行的行号。
FNR:各文件分别计数的行号
FILENAME:当前文件名
ARGC:命令行参数的个数
ARGV:数组,保存的是命令行所给定的各参数
内置变量NR
内置变量NR表示每一行的行号,内置变量NF表示每一行中一共有几列,那么,也就是说,我们可以通过下例中的方法,得到test文本中,每一行的行号以及每一行对应的列的数量。
或者,利用NR内置变量,先打印出行号,再打印出整行的内容,相当于为test中的每一行都添加了行号以后再进行输出,示例如下。
就是在打印 1 , ”符号,但是在调用 NR , NF 这些内置变量的时候,就没有使用”$”,
如果你有点不习惯,那么可能是因为你已经习惯了使用bash的语法去使用变量,在bash中,我们在引用变量时,都会使用$符进行引用,
但是在awk中,只有在引用1等内置变量的值的时候才会用到”$”,
引用其他变量时,不管是内置变量,还是自定义变量,都不使用”$”,而是直接使用变量名。
内置变量FNR
当我们使用awk同时处理多个文件,并且使用NR显示行号的时候,效果如下图。
从返回结果可以看出,awk处理多个文件的时候,如果使用NR显示行号,那么,多个文件的所有行会按照顺序进行排序。
可是,如果我们想要分别显示两个文件的行号,该怎么办呢,这个时候就会用到内置变量FNR,效果如下。
它的作用就是当awk处理多个文件时,分别对每个文件的行数进行计数。
内置变量RS
现在,我们来看看RS这个变量,我们说了,RS是输入行分隔符,如果不指定,默认的”行分隔符”就是我们所理解的”回车换行”。
假设,我们不想以默认的”回车换行”作为”行分隔符”,而是想使用空格作为所谓的行分隔符,也就是说,我们想让awk认为,每遇到一个空格,就换行,换句话说,我们想让awk以为每次遇到一个空格就是新的一行。那么我们该怎么做呢?示例如下。
如上图所示,我们先使用了默认的”回车换行”作为”行分隔符”输出了test文本,这时显示文本一共有2行。
而后来,我们又指定了使用”空格”作为”行分隔符”输出test文本,这时显示文本一共有8行。
看到了吗?当我们指定使用空格作为”行分隔符”时,在awk解析文本时,每当遇到空格,awk就认为遇到的空格是换行符,于是awk就将文本换行了,而此时人类理解的”回车换行”,对于awk来说并不是所谓的换行符,所以才会出现上图中第4行的现象,即使从人类的角度去看是两行文本,但是在awk的世界观里,它就是一行。
默认情况下,awk使用”回车换行”作为”行分隔符(换行符)”,此时,人类的世界观与awk的世界观是一致的,因为我们和awk都认为,遇到回车换行,就表示当前行结束,开始新的一行。
而当我们指定了特定的”行分隔符”时,比如空格,那么当awk遇到空格时,就认为当前行结束了,新的一行开始了,此时,awk的世界观与人类的世界观已经不同,人类仍然认为”回车换行”才是新的一行,awk却认为”回车换行”并不是新的一行的开始,所以,从上图中返回的信息中,我们可以看到,人类所以为的”两行”,共用了一个行号,awk认为它们就是第4行。
这就是输入行分隔符的使用方法。
内置变量ORS
在理解”输出行分隔符”ORS之前,请先理解刚才描述的”输入行分隔符”RS,否则理解起来可能比较困难。
默认情况下,awk将人类眼中的”回车换行”,当做”输出行分隔符”,此时,awk的”世界观”与人类的”世界观”是相同的。
现在,我们改变一下awk的想法,我们让awk认为,”+++”才是真正的输出行分隔符,示例如下图
在没有指定输出行分隔符之前,awk跟人类的逻辑思维是一样一样的,当人类想要换行的时候,就会”另起一行”(回车换行),awk也是一样的,当它在输出文字的时候,如果想要换行,就会”另起一行”(回车换行), 可是,如果我们指定了”输出行分隔符”为”+++”,那么,当awk在输出文字的时候,如果想要换行,就会”另起一行”(+++),所以,对于awk来说,它完成了”另起一行”的动作,只不过,它所认为的”另起一行”的动作就是输出”+++”,而不再是原来的输出” 回车换行”,所以,从人类看到的”表象上”,awk并没有换行,那是因为我们还是以”回车换行”作为换行的标准,而awk已经变了,它认为,”+++”就是换行的标准。
我们把刚才学到的”输入换行符”和”输出换行符”同时使用,看看是什么效果,示例如下。
内置变量FILENAME
FILENAME这个内置变量,从字面上,就能看出是什么意思,没错,就是显示文件名,演示效果如下。
内置变量ARGC与ARGV
ARGC内置变量表示命令行参数的个数
ARGV内置变量表示的是一个数组,这个数组中保存的是命令行所给定的参数。
上图中,我们先使用BEGIN模式,输出一个字符串”aaa”,然后,传入两个文件的文件名作为参数,我们发现,BEGIN模式正常执行了打印操作,输出了”aaa”字符串 ,我们使用同样的命令,同样使用BEGIN模式,只不过,这次不只打印”aaa”,还打印ARGV这个数组中的第二个元素的值。
我说已经说过,ARGV内置变量表示的是一个数组,既然是数组,就需要用上图中的下标的方式,引用对应元素的值,因为数组的索引都是从0开始的,所以,ARGV[1]表示引用ARGV数组中的第二个元素的值,从返回结果可以看出,ARGV[1]对应的值为test1,同理,我们又使用第三条命令,多打印了一个ARGV[2]的值,发现ARGV[2]对应的值为test2,这个时候,你明白ARGV内置变量的含义了吗,说白了,ARGV内置变量表示的是:所有参数组成的数组。那么细心的你一定会问了,ARGV[0]对应的是哪个参数呢,我们来打印一下。
第一个参数是awk命令本身,awk就是这么规定的,’pattern{ action }’并不被看做是参数,awk被看做为参数。
在刚才的例子中,应该有三个参数,awk、test1、test2,这三个参数作为数组的元素存放于ARGV中,现在,而ARGC则表示参数的数量,也可以理解为ARGV数组的长度。示例如下
自定义变量
自定义变量,顾名思义,就是用户定义的变量,有两种方法可以自定义变量。
方法一:-v varname=value 变量名区分字符大小写。
方法二:在program中直接定义。
我们来看一些小例子,即可明白上述两种方法。
通过方法一自定义变量。
这种方式,与设置内置变量的值的方法是一样的。
使用方法二自定义变量,直接在program中定义即可,但是注意,变量定义与动作之间需要用分号”;”隔开。
当然,我们也可以一次性定义多个变量
第一种方法虽然看上去比较麻烦,但是这种方法也有自己的优势
当我们需要在awk中引用shell中的变量的时候,则可以通过方法一间接的引用。举例如下
printf命令详解
printf命令的作用是按照我们指定的格式输出文本。
而提到输出文本,echo命令,我们来对比一下,echo与printf有什么不同
从上述示例中可以看出,在输出文本时,echo命令会对输出的文本进行换行,而printf命令则不会对输出的文本进行换行,那么,如果我们想要利用printf将输出的文本换行,使用转义符\n ,示例如下
假设,我们有一串文本需要输出,如下
现在我们有一个小需求,就是将上述文本按照空格分段,每段单独输出在一行里面。
上述示例中,命令1与命令2在输出文本时,都使用了转义字符\n将文本换行了,但是,在写命令1与命令2的时候,我的内心是崩溃的,因为,上述示例还算简单,我只是将5段文本分成5行输出即可,但是,如果是一个100段的文本呢?难道我们要在每一段中都添加一个转义字符”\n”,那我的手不就打残了,而命令3则不同,我只是通过printf指定了一个固定的”格式”,后面的每一段文本都按照指定的格式进行了换行,即使有10000段文本需要换行,我也不用担心手残的问题了,从这个小例子中,我们就能体会到printf的格式化能力,上图中命令3中使用到的”格式”会在后面进行详细的描述,不要着急。
printf命令的语法如下
printf “指定的格式” “文本1” “文本2” “文本3” ……
上述语法中的每一个”文本”都会被当做参数项传入printf命令,而每个被传入的参数都会按照指定的”格式”被”格式化”。
命令3中的”%s\n”即为我们指定的”格式”,而后面的每一段字符串,都被当做参数传入到了printf命令中,并按照我们指定的格式进行了格式化。那么,我们现在来详细的解释一下上图中的”%s\n”是什么意思。
我们先说说”%s”是什么意思,”%s”是一个”替身演员”,我们使用”%s”代替传入的参数,也就是说, “%s”代替了命令3中的abc,代替了def,代替了ghi,代替了每一个传入的参数,在我们指定的”格式”中,它代表了每一个传入的参数,所以,如果我们指定的格式为”%s\n”,当abc被当做参数传入printf命令时,printf就会把”%s\n”中的%s替换成abc,于是,abc就变成了我们指定的格式”abc\n”,最终printf输出的就是格式化后的”abc\n”,以此类推,每一段文本都被当做一个参数传入printf命令,然后按照指定的格式输出了。
而”替身演员”只是我给”%s”起的一个外号,它的真名叫”格式替换符”,而printf中,”格式替换符”不只有”%s”一种,”%s”代替了每一个传入的参数,并将他们转化成了”字符串类型”,我们再来认识一个新的替身演员,”%f” ,”%f”也代替了每一个传入的参数,与”%s”不同的是,”%f”会将每一个传入的参数转换成”浮点类型”,我们来看一个小例子。
上例中,我们分别使用了”%s”替换符和”%f”替换符格式化了相同的内容,但是格式化后的结果却不同。
“%f”自动将传入的数字添加了小数点,将传入的数字参数替换成了浮点数。
常用的格式替换符总结如下。
%s 字符串
%f 浮点格式(也就是我们概念中的float或者double)
%b 相对应的参数中包含转义字符时,可以使用此替换符进行替换,对应的转义字符会被转义。
%c ASCII字符。显示相对应参数的第一个字符
%d, %i 十进制整数
%o 不带正负号的八进制值
%u 不带正负号的十进制值
%x 不带正负号的十六进制值,使用a至f表示10至15
%X 不带正负号的十六进制值,使用A至F表示10至15
%% 表示”%”本身
说完了”格式替换符”,再来说说”转义字符”,刚才的示例中,我们只用到了”\n”这个转义符,还有很多其他的转义符,printf中的转义字符与其他程序中的转义字符没有什么不同,此处我们只是总结出来,方便大家使用。printf常用的转义符如下。
\a 警告字符,通常为ASCII的BEL字符
\b 后退
\c 抑制(不显示)输出结果中任何结尾的换行字符(只在%b格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略
\f 换页(formfeed)
\n 换行
\r 回车(Carriage return)
\t 水平制表符
\v 垂直制表符
\ 一个字面上的反斜杠字符,即”\”本身。
\ddd 表示1到3位数八进制值的字符,仅在格式字符串中有效
\0ddd 表示1到3位的八进制值字符
假设,我想为每个传入的参数添加一对”括号”,并且括号内侧需要有空格,那么我们可以使用如下命令。
假设,我们想要将每个传入的参数使用”制表符”隔开,那么,可以使用如下命令。
刚才的举例中,我们使用到的”格式”其实很简单,每个”格式”中都只用到了一个”格式替换符”,现在,我们扩展一下,在格式中设置多个”格式替换符”试试,示例如下
看完上图,我们所指定的”格式”中所包含的”格式替换符”的数量,就代表每次格式化的参数的数量,每个”格式替换符”与参数都是一一对应的,上图中,指定的”格式”中包含两个”格式替换符”,那么printf每次进行”格式化”操作时,就会传入两个参数,然后前一个参数对应第一个替换符,后一个参数对应第二个替换符,当本次格式化操作完成以后,再传入下一波参数,示意图如下
我们再验证一遍上述理论,把之前的格式改为如下图中的模样
按照之前的理论,因为”格式”中包含3个”格式替换符”,所以每轮格式化都可以一次性格式化3个参数,于是,第一次格式化操作将”%s %s %s\n”替换成了”a b c\n”,第二次格式化操作将”%s %s %s\n”替换成了”d e f\n”,格式化后的输出入上图所示。
awk格式化
利用awk中的printf动作,即可对文本进行格式化输出,printf动作的用法与printf命令的用法非常相似,只是有略微的不同而已,不过,我们还是从最简单的示例开始看起,首先对比一下print动作与printf动作的区别,示例如下
没错,printf动作与printf命令一样,都不会输出换行符,默认会将文本输出在一行里面。
使用”格式替换符”来指定一下$1的格式,示例如下。
只是printf动作与printf命令在语法上唯一的不同点就是,在使用printf动作时,指定的”格式”与列($1)之间需要用”逗号”隔开,而使用printf命令时,指定的格式与传入的文本不需要使用”逗号”隔开,如下图所示
其实,它们还有一些其他的不同之处,我们在使用printf命令时,当指定的格式中只有一个”格式替换符”,但是传入了多个参数时,那么这多个参数可以重复的使用这一个格式替换符,示例如下
但是在awk中,我们则不能这样使用,在awk中,格式替换符的数量必须与传入的参数的数量相同,换句话说,格式替换符必须与需要格式化的参数一一对应,示例如下。
我们来总结一下,在awk中使用printf动作时,需要注意以下3点。
1)使用printf动作输出的文本不会换行,如果需要换行,可以在对应的”格式替换符”后加入”\n”进行转义。
2)使用printf动作时,”指定的格式” 与 “被格式化的文本” 之间,需要用”逗号”隔开。
3)使用printf动作时,”格式”中的”格式替换符”必须与 “被格式化的文本” 一一对应。
可以利用格式替换符对文本中的每一列进行格式化,示例如下。
我们可以利用awk的内置变量FS,指定输入字段分隔符,然后再利用printf动作,进行格式化,示例如下。
上例完美的体现了awk的格式化能力,因为awk本身负责文本切割,printf动作负责格式化文本。