Loading

三剑客基础详解(grep、sed、awk)

三剑客基础详解

三剑客之grep详解

通配符与正则表达式这两口子可以说贯穿三剑客始终,甚至时贯穿linux始终,这样说,我觉得并不夸张。因此在写三剑客之前,先捋一捋这些这些知识点就很有必要了。

相对而言正则用于三剑客多一些,通配符用于Linux命令行多一些。

1.通配符

通配符 描述
* 任意多个字符
任意单个字符
. 当前目录
.. 上级目录
命令分隔符
~ 当前用户家目录
$ 引用变量
! 逻辑运算非
&& 前一个命令执行成功,则执行后面的命令
|| 当前命令执行失败,则执行后面命令
' 单引号,原样输出,所见即所得
" 双引号,可以置换变量的值,不加引号相当于双引号
` 反引号中的内容会被优先解析
- 之前所在路径
{} shell脚本命令组合;命令行中内容序列
> 重定向覆盖
>> 重定向追加

2.基础正则

  • 锚定行首,如word表示以word开头的行
  • $锚定行首,如word$表示以word结尾的行
  • ^$则表示空行
  • . 任意单个字符
  • *匹配其前面字符任意多次
  • .*表示匹配所有字符
  • \ 转义字符,使得特殊字符失去特殊含义
  • []匹配范围内的单个字符
  • [^]匹配范围外的单个字符
  • 指定匹配次数的范围,即分组
    1. \{n,m\} 匹配前面字符n到m次
    2. \{n,\} 匹配前面字符至少n次
    3. \{,m\}匹配前面字符至多m次
    4. \{n\} 匹配前面字符n次

在扩展正则中可以去掉转义符,即egrep 或者 grep -E

3.grep 讲解

3.1语法格式

grep [OPTIONS] [PATTERN] [FILE..]

grep [选项] [过滤的内容] [源文件]

3.2常用参数

  • -n 显示过滤内容所在行数
  • -o 只显示匹配到的内容,不会按行输出
  • -i 忽略大小写
  • -v 反向选择,显示未匹配到的内容
  • -E 支持拓展正则
  • -A 匹配到的行后面几行,即After
  • -B 匹配到的行前面几行,即Before
  • -C 配到的行前后几行,即context上下文
#准备好实验内容
randolf@localhost:~/test $ ls && cat sed2.txt 
inittab  sed2.txt  test.md5  test.txt
Oh/my/god,dear me!
hello word
you are so cute
what do you think of the party
I am so glad to tell you that I am crazy about Linux!
Linux is better than Windows much more.
#显示以hello开头的行,并且显示其所在行数
randolf@localhost:~/test $ grep -n "^hello" sed2.txt 
2:hello word
#显示以cute结尾的行,并且只打印匹配到的内容
randolf@localhost:~/test $ grep -o "cute$" sed2.txt 
cute
#显示不包含单词you的行
randolf@localhost:~/test $ grep -v "you" sed2.txt 
Oh/my/god,dear me!
hello word
Linux is better than Windows much more.

4.拓展正则

  1. + 重复匹配前面字符1次及1次以上(区别于*匹配0次至多次)
  2. ? 重复匹配前面字符0次或者1次(.表示任意一个字符,不是匹配)
  3. | 可以同时过滤多个字符
  4. () 分组过滤,向后引用(也叫方向引用),将组中的内容作为一个整体来匹配,突破了只能匹配前面单个字符的限制,常常结合分组的使用
#新建测试文本test
randolf@localhost:~/test $ cat > test<<EOF
> gods
> gds
> goods
> gooods
> goads
> EOF
#分别演示+ ? . 的用法
randolf@localhost:~/test $ egrep "go?ds" test 
gods
gds
randolf@localhost:~/test $ egrep "go+ds" test 
gods
goods
gooods
randolf@localhost:~/test $ egrep "go.ds" test 
goods
goads
#演示 | 的多字符过滤
randolf@localhost:~/test $ egrep "gds|goods" test 
gds
goods
#向后引用结合分组的演示
randolf@localhost:~/test $ echo "goodsgoods" >> test
randolf@localhost:~/test $ grep -E '(goods){2}' test 
goodsgoods

5.POSIX字符类

括号类 含义
[:alnum:] 字母数字字符
[:alpha:] 字母字符
[:digit:] 数字字符
[:graph:] 非空白字符(非空格、控制字符等)
[:lower:] 小写字母
[:print:] 与[:graph:]相似,但是包含空格字符
[:punct:] 标点字符
[:upper:] 大写字母
[:xdigit:] 允许十六进制的数字
[:cntrl:] 控制字符

字符类用的较少,放在这里仅作备用

三剑客之sed讲解

sed全称为Stream Editor即流编辑器,用于过滤和转换文本,常用的功能包括增删改查,过滤,取行。

1.sed的执行流程

sed作为流编辑器仅支持单行操作,每次启动sed进程时,linux会为它在内存中单独分配一个专属的空间,我们称其为模式空间。sed每次只读取文本中的一行到模式空间中,执行完指定的sed内置命令后,默认将模式空间中的文本打印到屏幕上,然后删除模式空间中的文本,再读取原文本中的第二行,重复上面的操作,直至文本的最后一行。

sed1

2.语法格式

sed [option] [AddressCommand] [input-file]

2.1option:

  • -n 静默模式
  • -i 编辑原文件
  • -r 支持拓展正则

2.2 Address:

这里的地址我们指sed所筛选的目标行,支持模式匹配,即使用通配符等。

地址表示形式 描述
1 操作第1行
1,3 操作第1到3行
1,+3 操作第1行以及后面3行(1到4行)
1,$ 操作第1行到最后一行
1~2 通用(nm),从第n行开始,步进为进行m筛选,如12表示第1、3、5....行

以上地址表示皆支持模式匹配,如

地址表示形式 描述
/word/ 操作所有匹配到word这个单词所在行
/word/,/Linux/ 操作匹配到word的行一直到匹配到Linux的行,遵循懒汉模式的搜索匹配
/word/,+3 操作匹配到word的行以及后面三行(从第一次匹配到word开始)
/word/,$ 操作匹配到word的行一直到最后一行(从第一次匹配到word开始)

懒汉模式匹配:第一次搜索到的结果(word)作为开始,并且第一次搜索到的结果作为(Linux)作为结尾

2.3 Command

此处命令指的是sed编辑器的内置命令,而不是linux的系统命令

现在创建如下文件touch /home/randolf/test/sed.txt

并且写入内容,作为实验文本:

cat >sed.txt<<EOF
>0.I am the whole word
>1.hello word
>2.you are so cute
>3.what do you think of the party
>4.I am so glad to tell you that I am crazy about Linux!
>5.Linux is better than Windows much more.
>EOF

地址表示形式与命令的搭配太多不可能一一演示,下面选取典型场景,为大家演示

  • p 打印行

    #打印第2到3行
    randolf@localhost:~/test $ sed '2,3p' sed.txt
    0.I am the whole word
    1.hello word
    1.hello word
    2.you are so cute
    2.you are so cute
    3.what do you think of the party
    4.I am so glad to tell you that I am crazy about Linux!
    5.Linux is better than Windows much more.
    

    咦?怎么打印这么多东西。我明明只选取2到3行呀。还记得上面说的嘛,“执行完指定的sed内置命令后,默认将模式空间中的文本打印到屏幕上”,没错,sed除了将我们选取的内容打印一遍,还没有忘记自己本来的职责,即打印一遍模式空间中的内容。所以就看了以上打印内容,2到3行打印了两遍。那如果不想要sed默认打印模式空间咋办?真相只有一个!选项-n,他表示静默模式,即不再默认打印模式空间中的内容,试试看喽。

    randolf@localhost:~/test $ sed -n '2,3p' sed.txt
    1.hello word
    2.you are so cute
    #顿时清爽多了,舒服
    
    #打印匹配到word的行一直到匹配到Linux的行,遵循懒汉模式的搜索匹配
    randolf@localhost:~/test $ sed -n '/word/,/Linux/p' sed.txt 
    0.I am the whole word
    1.hello word
    2.you are so cute
    3.what do you think of the party
    4.I am so glad to tell you that I am crazy about Linux!
    

    对比原来的文本中,含单词word的行有两行,同样含单词Linux的行也有两行,而作为起始行与结束行的都时匹配到的第一个结果,即遵循懒汉模式

  • s 替换行

    #将第一行中"the whole word"替换为"a student"
    randolf@localhost:~/test $ sed 's/the whole word/a student/' sed.txt 
    0.I am a student
    1.hello word
    2.you are so cute
    3.what do you think of the party
    4.I am so glad to tell you that I am crazy about Linux!
    5.Linux is better than Windows much more.
    #这里替换的只是sed模式空间中的内容,若想要直接操作员文本,可以使用选项-i
    randolf@localhost:~/test $ sed -i 's/the whole word/a student/' sed.txt
    #当我们直接操作原文本时,同样也是不打印模式空间的
    randolf@localhost:~/test $ cat sed.txt 
    0.I am a student
    1.hello word
    2.you are so cute
    3.what do you think of the party
    4.I am so glad to tell you that I am crazy about Linux!
    5.Linux is better than Windows much more.
    

    sed的内置命令s中的边界符除了“/”,还可以使用“#”、"@"等特殊符号,常用于避免和原文本中的符号产生冲突,虽然也可以使用转义符避免冲突,不过转义符可能让你眼花缭乱。。

    举个例子

    #使用vim将刚刚的文本第一行修改为"0.Oh/my/god,dear me!",并执行复制cp sed.txt sed2.txt
    #目标:将Oh/my/god修改为honey
    #1.使用"@"作为定界符修改sed.txt
    randolf@localhost:~/test $ sed 's@Oh/my/god@honey@' sed.txt |head -n 1
    0.honey,dear me!
    #2.使用"/"作为定界符修改sed2.txt
    randolf@localhost:~/test $ sed 's/Oh\/my\/god/honey/' sed2.txt |head -n 1
    0.honey,dear me!
    #虽然都能实现操作,可这视觉效果实在不堪,让我这老花眼晕一会。。。
    
  • d 删除行

    #读入模式空间后直接删除读入的行,不让他打印到屏幕上来
    #删除1到3行
    randolf@localhost:~/test $ sed '1,3d' sed.txt 
    3.what do you think of the party
    4.I am so glad to tell you that I am crazy about Linux!
    5.Linux is better than Windows much more.
    #操作原文本,删除第一行
    #先瞄一眼,未删除之前第一二行是啥
    randolf@localhost:~/test $ sed -n '1p' sed.txt| head -n 2
    0.Oh/my/god,dear me!
    1.hello word
    #执行删除命令
    randolf@localhost:~/test $ sed -i '1d' sed.txt 
    randolf@localhost:~/test $ sed -n '1p' sed.txt| head -n 1
    1.hello word
    #看到了吧,原文件中第1行成功删除,原本的第2行上位成第一行
    
  • a 目标行后面追加一行

    #最后一行后面添加一行内容,"The earth is beautiful"
    randolf@localhost:~/test $ sed -i '$a The earth is beautiful' sed.txt 
    randolf@localhost:~/test $ sed -n '$p' sed.txt 
    The earth is beautiful
    #成功!!!
    
  • i 目标行前面追加一行

#第一行前面添加一行内容,"This is a start line added just now!"
randolf@localhost:~/test $ sed -i '1i This is a start line added just now!' sed.txt 
randolf@localhost:~/test $ sed -n '1p' sed.txt 
This is a start line added just now!
#So easy!!!

三剑客之Awk

awk作为三剑客中最最复杂的老大哥,常常被称为awk语言,那么,详细解释是不可能的,这辈子都不可能的。。。聊点皮毛

1.awk初见面

1.1awk的简单介绍

1977年由Alfred Aho 、Peter Weinberger 和 Brian Kernighan这三位同学创造,并以其名字的首字母命名该语言。

版本:awk同样分为不同的版本,如gawk、dawk、pawk,mawk等,在Linux里常使用的是gawk(红帽系列)、和mawk(乌班图系列)。我在使用的是rhel的社区版centos7,所以记录的也将是gawk的使用。

1.2 awk的工作机制

类似其他两位剑客(sed、grep),awk也是按行读取,过滤用户给定的匹配模式,并且执行特定的处理动作

组成部分(重要):pattern {action}

即匹配的模式(支持正则)和对匹配到的内容所执行的处理动作

两个特殊的模式:BEGIN END顾名思义,这两家伙分别表示在读取模式所匹配的数据之前执行,和在全部读取完数据之后执行

两个注意点:1.若未给定模式,则匹配所有 2.若没有指明处理行为,则默认执行打印动作print

毫不夸张地说,理解awk的组成,也就掌握其一半的使用能力了。

2.常用选项

Usage: awk [POSIX or GNU style options] -f progfile [--] file ...
Usage: awk [POSIX or GNU style options] [--] 'program' file ...
POSIX options:		GNU long options: (standard)
	-f progfile		--file=progfile #从脚本中加载awk的命令
	-F fs			--field-separator=fs #指定分隔符
	-v var=val		--assign=var=val   #处理动作执行前设置一个变量val


-f加载的脚本中放置的是awk结构的命令,包括 pattern {action}

当需要指定多个分隔符时,可以以通过内置变量FS设置

多个动作处理条件以空格隔开,如{action1} {action2} {action3}.....

#案例素材
randolf@zrm:~/test $ cat test 
centos|ubuntu|arch
apple,orange,banana dog cat
#以|为分隔符,打印$3、$NF
randolf@zrm:~/test $ awk -F '|' '{print $NF}' test
arch
apple,orange,banana dog cat
randolf@zrm:~/test $ awk -F '|' '{print $3}' test
arch

NF为内置变量,表示当前记录的个数,那么思考一下,$3的打印结果为什么第二行为空呢?
我们前面提到awk是按行读取的,所以对于awk来说每一行就是一个操作范围,第一行被"|"分为三个字段,因此记录个数为3,即$NF的值是3,所以第一行的值输出相同;而第二行没有分隔符"|",所以整行只有一个字段记录,因此$NF的值为1,输出整行,而$3在第二行不存在,所以输出空白行。
#重新编辑test文本内容
randolf@zrm:~/test $ cat test
centos|ubuntu|arch
apple,orange,banana dog cat

nihaoa,xiongdi

woshiyizhishuaiguo
#编写简单脚本供awk调用
randolf@zrm:~/test $ echo "/^$/ {print \"我是一只空行\"}" > haha   #双引号需要转义
randolf@zrm:~/test $ cat haha 
/^$/ {print "我是一只空行"}    #注意中间有空格
randolf@zrm:~/test $ awk -f awk.sh test
我是一只空行
我是一只空行
#BEGIN字段应用举例(2014年兄弟连 沈超李明老师经典教程)
randolf@zrm:~/test $ awk  '{FS=":"} {print $1}' /etc/passwd |head -4
root:x:0:0:root:/root:/bin/bash
bin
daemon
adm
randolf@zrm:~/test $ awk  'BEGIN{FS=":"} {print $1}' /etc/passwd |head -4
root
bin
daemon
adm
#内容太长仅以4行为例,看到了么,未加BEGIN时第一行没有被分隔,哈哈,是不是蒙圈了?其实很好理解,awk会事先读取一行文本的内容,然后才会去找前面的条件,此处为设定的分隔符,嗯哼?想一想前面提到过的BEGIN的定义,BEGIN做两大特殊模式之一,表示在读取模式所匹配的数据之前执行,所以呢,加上BEGIN后,awk会先读取BEGIN后的条件。^_^

3.内置变量

变量名称 描述
ARGC 命令行参数个数
FILENAME 当前输入文档的名称
FNR 当前输入文档的记录编号,多个输入文档时有用
NR 输入流当前记录编号
NF 当前记录的字段个数
FS 字段分隔符
OFS 输出字段分隔符,默认是空格
ORS 输出记录分隔符,默认换行符 \n
RS 输入记录分隔符,默认换行符

4.表达式与操作符

4.1表达式的组成部分

表达式由变量,常量,函数,正则表达式,操作符组成
awk中变量有字符变量和数字变量,如果在awk中定义变量没有初始化,则初始化值为空字符或0。字符操作必须加引号,如 a="hello word"

4.2 常见操作符

+ - * / (加减乘除。。)

% 取余 ^ 幂运算 ++ -- += -= *+ /= '>'

<       >=      <=       == 等于   !=     ~匹配     !~不匹配   &&与      ||或
#操作符简单举例
randolf@zrm:~/test $ echo test  | awk 'x=2 {print x+=1}'
3
randolf@zrm:~/test $ echo hello | awk 'x=1,y=3 {print x*2,y*3}'
2 9
#表达式与模式END结合使用
randolf@zrm:~/test $ awk '/^$/ {x+=1} END {print x}' test
2
#打印root用户的UID,cat一下知道,/etc/passwd文件中第三个字段是用户的UID
randolf@zrm:~/test $ awk -F: '$1~/root/ {print $3}' /etc/passwd
0


posted @ 2019-01-30 01:21  流火无心  阅读(522)  评论(0编辑  收藏  举报