vim与正则表达式

文本处理工具与正则表达式

1 文本处理工具VIM

1.1 软件简介

纯文本文件:由ASCII, Unicode 或其它编码的纯文字的文件

文本文件编辑工具:

  • 全屏编辑
    nano(字符工具), gedit(图形化工具), vi,vim
  • 行编辑器:sed

vi——Visual editor,文本编辑器

vim——VIsual editor iMproved ,和vi 使用方法一致,但功能更为强大

1.2 使用 vim 初步

vim分很多种工作模式,操作之前首先明确当前处于哪个模式

首要快捷键:模式之间相互切换

vim 命令格式

vim [OPTION]... FILE...
+#             打开文件后,让光标处于第#行的行首,+默认行尾
+/ PATTERN     让光标处于第一个被PATTERN匹配到的行行首
-b file        二进制方式打开文件 
-d file1 file2…比较多个文件,相当于 diff
-m file        只读模式打开文件
-e file        直接进入ex模式,相当于执行ex file

说明:

  • 如果该文件存在,文件被打开并显示内容
  • 如果该文件不存在,当编辑后第一次存盘时创建它

1.3 三种主要模式和转换

  • 命令模式:默认模式,可以实现移动光标,剪切/粘贴文本
  • 插入(Insert)或编辑模式:用于修改文本
  • 扩展命令(extended command )或命令行模式:保存,退出

打开vim默认进入命令模式,所有按键操作在屏幕上不可见!!!

  • 命令模式>>插入模式

    i 在当前光标'所在处输入          #当前光标处插入
    a 在当前光标'所在处后面输入       #紧跟当前光标之后
    
    I 在当前光标所在行的'行首输入      #本行首
    A 在当前光标所在行的'行尾输入      #本行尾
    
    o 在当前光标'所在行的下一行新建行   #下一行新建行首
    O 在当前光标'所在行新建行          #挤进当前行
    
  • 从插入模式返回

    按 esc
    
  • 命令模式>> 扩展命令模式

    按 :
    
  • 从扩展命令模式返回

    按 esc 或 enter  #一般按enter即为在扩展命令模式执行了一条命令
    

1.4 命令模式

命令模式功能强大,只是按键时看不到输入内容,所以需要大量的记忆才能更好的使用

1.4.1 退出VIM

ZZ 保存并退出

ZQ 不保存退出

1.4.2 光标跳转

字符间跳转:

h: 左 l: 右       j: 下  k: 上

#指定移动步数:#cmd
如 5k 表示直接上移5行
#直接数字加方向键也能实现同样功能!!!!!!

单词间跳转:

w:     下一个单词的词首
e:当前或下一个单词的词尾
b:当前或#前一个单词的词首(back往回走)
#cmd:由#指定一次跳转的单词数

句间移动:

) 下一句          ( 上一句
#判断依据为句号. 而且必须有至少一个空格,如a.b不能算是句号

段落间移动:

} 下一段          { 上一段
#判断依据为空行

行间移动:

1G或gg  第一行
G       最后一行
#跳转至第N行:
NG 或者 EX模式下输入N

行首行尾跳转:

^ 跳转至行首的第一个非空白字符
0 跳转至行首
$ 跳转至行尾

当前页跳转:

H:页首(head)   M:页中(middle)   L:页底(last)
#下述移动是指上下滑动调整查看效果,而不是编辑内容!!!!!!!!!
zt:将光标所在当前行移到屏幕顶端
zz:将光标所在当前行移到屏幕中间
zb:将光标所在当前行移到屏幕底端

命令模式翻屏操作:

直接pgup、pgdown即可

1.4.3 字符编辑

在命令模式下也能进行部分编辑操作

x   删除光标处的字符
#x  删除从光标处起始的全部#个字符          #累计删除!!
xp  交换光标所在处的字符及其后面字符的位置   #位置替换
~   转换大小写
J   删除 当前行尾的 换行符                #随便光标处在哪儿

1.4.4 替换命令(replace)

r 替换光标所在处的字符(定点改错)       #先输入r,再输入想要替换的内容
R 切换成REPLACE模式,按ESC回到命令模式   #替换模式,输入内容将会覆盖原文件内容

1.4.5 删除命令(delete)

d  删除命令   #可结合光标跳转字符,实现范围删除
d$ 删除到行尾  = D
d^ 删除到非空行首
d0 删除到行首
dw 删除单词
de 删除单词
db 删除前面的单词

dd:删除光标所在的行   #实际上是剪切
Ndd:删除 #从本行开始往下N行
dG:从本行开始往后全部删除
dgg:从本行开始往前全部删除

1.4.6 复制命令(yank)

y 复制,行为相似于d剪切

同理也可以结合光标跳转字符,实现范围操作

yy或Y  复制本行
#yy   复制多行(从本行往下#行)

!!!应注意:单独按 d 或 y 是不起作用的

1.4.7 粘贴命令(paste)

小p 若缓存的是整行,则贴在光标下一行,否则跟在后方
大P 若缓存的是整行,则贴在光标上一行,否则跟在前方

1.4.8 改变命令(change)

与 d 的功能完全一致,但是完成操作后自动切换到插入模式

比如:

cc :即为删除本行并进入插入模式,cc=S

C:删除当前光标到行尾,并切换成插入模式,相当于c$

1.4.9 搜索///

#pattern可以是普通字符或正则表达式
/PATTERN:从当前光标所在处向文件尾部查找
?PATTERN:从当前光标所在处向文件首部查找

#出现多个结果之后,按回车 并用 n N 进行选择
n:与命令同方向
N:与命令反方向

1.4.10 撤销u与重复 .

u  撤销最近的更改
U  撤消 当光标落在这行之后 在此行进行的所有更改
#u 撤销之前的多次更改
Ctrl - r  #撤销 上一次“撤消”操作

.   #重复上一次操作!!!!!!!!!!!!!!!!!
N.  重复前一个操作N次

1.4.11 高级用法

<start position><command><end position>
#常见Command:y 复制、d 删除、gU 变大写、gu 变小写
比如 0y$ 命令
0 → 先到行头
y → 从这里开始拷贝
$ → 拷贝到本行最后一个字符
#因此0y$表示复制这一行 = yy

特别地:范围操作

di" 光标在" "之间,则删除" "之间的内容
yi( 光标在( )之间,则复制( )之间的内容
vi[ 光标在[ ]之间,则选中[ ]之间的内容
dtx 删除字符#直到遇见光标之后的第一个 x 字符  t=till
ytx 复制字符#直到遇见光标之后的第一个 x 字符

1.5 扩展命令模式(末行)

按“ : ”进入Ex模式 ,创建一个命令提示符: 处于底部的屏幕左侧

ex模式基本命令

w  写(存)磁盘文件
wq 写入并退出
x  写入并退出
X  加密
q  退出
q! 不存盘退出,即使更改都将丢失
r file (输入)读指定文件内容到当前文件中
w file (输出)将当前文件内容写入另一个文件
!command      执行命令   #意思是不退出vim的情况下执行命令
r!command     读入命令的输出

ex模式高级用法

1.5.1 地址定界

以行为基本单位

:start_pos,end_pos cmd
#  具体第#行,例如2表示第2行
.  当前行
$  最后一行

N,M  表示第N行到第M行
%    全文, 相当于1,$
N,+M  表示第N行到第N+M行
.,$-1 当前行到倒数第二行

/pattern/     从当前行向下查找,直到匹配pattern的第一行,即:正则表达式
/pat1/,/pat2/ 从第一次被pat1模式匹配到的行开始,一直到第一次被pat2匹配到的行结束
N,/pat/       从指定行开始,一直找到第一个匹配patttern的行结束
/pat/,$       向下找到第一个匹配patttern的行到整个文件的结尾的所有行

地址定界后跟一个编辑命令,即可进行批量范围操作

d       删除
y       复制
w file  将范围内的行另存至指定文件中
r file  在指定位置插入指定文件中的所有内容

#比如2,5 d 表示删除2~5行

1.5.2 查找并替换 s///

s/要查找的内容/替换为的内容/[修饰符]
#直接s默认处理光标所在行,全文修改需要加%,即为%s///
#要查找的内容:可使用正则表达式模式
#替换为的内容:不能使用PATTERN,但可以使用\1, \2, ...等后向引用符号
             还可以使用“&”引用前面查找时查找到的整个内容
#修饰符:
   i  忽略大小写
   g  全局替换   #默认情况下,每一行只替换第一次出现的目标
   gc 全局替换,每次替换前询问

特别地,s///也可以换为s@@@...等其他字符

s@/etc@/var@g   #将光标所在行的全部/etc替换为/var
s#/boot#/#i     #将/boot替换为/ 且不区分大小写

1.5.3 个性化设置

  • 显示行号——显示:set number,简写 set nu
  • 设置光标所在行的标识线——启用:set cursorline,简写 set cul
  • 忽略大小写——启用:set ignorecase,简写 set ic
  • 自动缩进——启用:set autoindent,简写 set ai
  • 复制保留格式——启用:set paste
  • 显示Tab和换行符 ^I 和$显示——启用:set list
    • set et 表示用空格代替Tab
    • set ts=4 表示指定用4个空格替代Tab
  • 高亮搜索——启用:set hlsearch
  • 语法高亮——启用:syntax on
  • 设置文本宽度——set textwidth=65 (vim only)
  • .........

以上所有设置的关闭方式相同:set noCMD

永久保存设置,保存在配置目录

1.6 可视化模式

用方向键和其他字符跳跃快捷键模拟鼠标选中操作
被选中的文字可被删除,复制,变更,过滤,搜索,替换等

进入此模式的快捷键:

  • v 面向字符
  • V 面向整行
  • ctrl-v 面向矩形块
范例:在文件行首插入#字符

输入gg光标回到第一行
输入ctrl+v 进入可视化模式
输入 G 跳到最后1行,此时即选中了所有行
输入 I 切换至插入模式(I进入插入模式且在行首输入)
输入 #
按 ESC 键,插入完成

1.7 多文件模式

vim FILE1 FILE2 FILE3 ...
:next   下一个
:prev   前一个
:first  第一个
:last   最后一个
:wall   保存所有
:qall   不保存退出所有
:wqall  保存退出所有

1.8 多窗口模式

1.8.1 多文件分割

vim -o|-O FILE1 FILE2 ...
-o: 上下分割
-O: 左右分割(vim only)
在窗口间切换:Ctrl+w, Arrow

1.8.2 单文件窗口分割

单个文件切割后相当于两个窗口同时显示同一个文件

Ctrl+w,s:split, 水平分割
Ctrl+w,v:vertical, 垂直分割
ctrl+w,q:取消相邻窗口
ctrl+w,o:取消全部窗口
:wqall  :退出

1.9 vim的寄存器

寄存器即为多个剪贴板

  • 字母寄存器

有26个字母命名寄存器和1个无命名寄存器,常存放不同的剪贴版内容,可以不同会话间共享

寄存器为 "a   
使用时输入:数字 "a yy  
3"tyy 表示复制3行到t寄存器中

"tp   表示将t寄存器内容粘贴,位置为当前光标所在行下面

未指定,将使用无命名寄存器,即为普通的复制粘贴操作

  • 数字寄存器

还有10个数字寄存器,用0,1,…,9表示,0存放最近复制内容,1存放最近删除内容
当新的文本变更和删除时,1转存到2,2转存到3,以此类推

数字寄存器不能在不同会话间共享

1.10 标记和宏(macro)

作用是标记书签

输入ma :将当前位置标记为a
输入 'a 跳转到a标记的位置

26个字母均可做标记, mb 、 mc 等等

qa 录制宏 a,a为宏的名称
q 停止录制宏
@a 执行宏 a
@@ 重新执行上次执行的宏

录制宏 是录制位置还是录制一系列操作???

1.11 编辑二进制文件

涉及 xxd 命令

#以二进制方式打开文件
vim -b binaryfile
#扩展命令模式下,利用xxd命令转换为可读的十六进制
:%!xxd

#插入模式下,编辑二进制文件

#扩展命令模式下,利用xxd命令转换回二进制
:%!xxd -r
#保存退出

1.12 易错点&重点

  • vim不支持ERE!!!!!
    vim内使用set list显示不可见字符

练习题:

1、在vim中设置tab缩进为4个字符

配置文件路径为/etc/vimrc
set et
set ts=4

2、复制/etc/rc.d/init.d/functions文件至/tmp目录,替换/tmp/functions文件中的/etc/sysconfig/init为/var/log

[10:22:33root@CentOS8/~]# cp -a /etc/rc.d/init.d/functions /tmp
[10:22:58root@CentOS8/~]# ls /tmp/
functions           vmware-root_744-2957583465  vmware-root_795-4257200573
ks-script-m0p456v5  vmware-root_760-2990613113
[10:23:12root@CentOS8/~]# vim /tmp/functions 
#vim进入文件之后,执行下述命令
:%s@/etc/sysconfig/init@/var/log@g
#正确结果在63,64两行

3、删除/tmp/functions文件中所有以#开头,且#后面至少有一个空白字符的行的行首的#号

#首先明确符合条件的有30行
[10:38:55root@CentOS8/~]# grep -c "^#[[:blank:]]\+" /tmp/functions 
30
#vim进入文件之后,执行下述命令
:%s/^#[[:blank:]]\+//     第二处参数不输入表示替换为空,即删除   末尾不加g表示只处理行首
#确认已处理完毕
[10:53:52root@CentOS8/~]# grep -c "^#[[:blank:]]\+" /tmp/functions 
0

2 文本处理常用命令

2.1 文件内容查看命令

2.1.1 常规查看cat

cat 可以查看文本内容

cat [OPTION]... [FILE]...

-n:对显示出的每一行进行编号
-b:对非空行编号            命令 nl  显示行号,相当于cat -b
-s:压缩连续的空行成一行
-E:显示行结束符$
-T:显示TAB为^I
-A:显示所有控制符

#其他相关命令:
tac '按照行号逆向显示文本内容
rev '将同一行的内容逆向显示

特别地,若无指定文件或文件名为 - ,表示从标准输入接收信息

[20:28:01root@CentOS8data]#cat
tttt
tttt
reeghwwh
reeghwwh
^C
[20:32:02root@CentOS8data]#cat -
thsrb
thsrb
esrhaeth
esrhaeth
^C              #使用Ctrl+C 退出

Windows中的文本文件与Linux中格式有所不同

  • Windows换行——回车+换行
  • Linux换行——换行(\n)

对于一些类似换行、Tab...等不可见字符,可使用 -A 选项进行显示

[20:37:09root@CentOS8data]#cat nn.txt 
qwe
asdf   
zxc     
[20:37:19root@CentOS8data]#cat -A nn.txt 
qwe$
asdf   $      #此处有空格
zxc^I^I$      #此处有Tab

两种逆向显示:

[20:43:54root@CentOS8data]#cat cat.log 
123
456
789
[20:44:02root@CentOS8data]#tac cat.log 
789
456
123
[20:44:07root@CentOS8data]#rev cat.log 
321
654
987

2.1.2 分页查看more与less

文本篇幅较大时,用cat查看不太方便,此时应使用more命令

  • 进入more终端之后的快捷键

    换行  -- 回车键
    上翻页 -- b
    下翻页 -- 空格键
    

使用more命令查看,移动到最后一行时自动退出

为了反复查看文件,可使用less命令(less终端内不显示进度百分比

  • 进入less终端之后的快捷键

    #换行翻页与more相同
    跳到首行 -- g
    跳到末行 -- G
    搜索关键词 -- /keyword
    退出终端 -- q
    

与管道配合使用

若已知输出结果会是大篇幅内容,可提前用管道连接more或less

例如:ls -R /etc/ | less

2.1.3 按行抽取head与tail

head tail
默认值 -n 10 输出前10行 输出后10行
-n +5 输出前5行 第5行往后全部输出
-n -5 砍掉最后5行 输出后5行

大篇幅文件,若只需要查看前几行,可使用head命令

不指定行数,head默认输出前10行

head [-n N] file   N为指定的行数
head [-c N] file   N为指定的字节数
[10:00:26root@CentOS8~]#head -n 5 /etc/init.d/functions 
# -*-Shell-script-*-
#
# functions This file contains functions to be used by most or all
#       shell scripts in the /etc/init.d directory.
#
[10:28:55root@CentOS8~]#echo 12345 | head -c 3
123

特别地,行数为负数时,如head -n -5 表示 去除最后5行


与之相反,tail命令则表示显示文件指定的最后几行

不指定默认输出最后10行,同样也支持 -c 字节数

[10:11:41root@CentOS8~]#tail -n 5 /etc/shadow
sshd:!!:18445::::::
avahi:!!:18445::::::
tcpdump:!!:18445::::::
jacklee:$6$QlCPFppDpXzzYYRB$Zot.igaBz4y8VWcAOKVjEfo54fnvC9GKd2DwmxOTXFCES5UU0zolUzUduI/607qQW6iLV04QFjxheuva/3lyd0:0:3:42:14:7:18545:
ass:!!:18468:0:99999:7:::
[10:29:45root@CentOS8~]#echo 12345 | tail -c 3
45
#此处 -c 3 表示输出最后3个字符
#因为echo命令输出普通字符默认最后有一个换行符,\n,占一个字节
#因此最后3个字节为 4 5 \n  

特别地,tail命令行数前使用 + 号,如 -n +3 表示从第3行往后全部输出

[10:23:50root@CentOS8~]#seq 9 | tail -n 3
7
8
9
[10:24:08root@CentOS8~]#seq 9 | tail -n +3
3
4
5
6
7
8
9

@@tail实用功能( -f 选项):持续监测文件是否有追加内容

终端1:ping www.baidu.com > /data/ping.log
终端2:tail -f /data/ping.log

#与tail -f功能类似的命令tailf
#唯一区别是 当文件不可访问时,tailf命令会自动退出

2.1.4 按列抽取cut

cut 命令以指定分隔符对文本文件或STDIN数据的每行内容进行切割

默认分隔符为TAB

cut [OPTION]... [FILE]...

三种类似功能:指定 字节b/字符c/字段f 序号或范围

-b 指定字节      -n 不分割多字节字符,如汉字   经常与 -b 组合使用 
-c 指定字符
-f 按指定分隔符进行分割   -d指定分隔符 与 -f组合使用

N   定位到第N个
N-  从第N个字节、字符、字段到结尾;
N-M 从第N个字节、字符、字段到第M个(包括M在内)字节、字符、字段;
-M  从第1个字节、字符、字段到第M个(包括M在内)字节、字符、字段。
#混合使用:2-4,7 = 2,3,4,7

不常用功能:-b 、 -c

[16:15:06root@CentOS8data]#cat f3
123456789
ABCDEFGHI
[16:15:10root@CentOS8data]#cut -b 5 f3
5
E
[16:17:23root@CentOS8data]#cut -b 5- f3
56789
EFGHI
[16:17:44root@CentOS8data]#cut -b 5-6 f3
56
EF
[16:17:49root@CentOS8data]#cut -b -5 f3
12345
ABCDE
                                        -c 与 -b 结果相同#是因为单个alnum恰好是一个字节
[16:17:54root@CentOS8data]#cut -c 2 f3
2
B
[16:18:22root@CentOS8data]#cut -c 2- f3
23456789
BCDEFGHI
[16:18:29root@CentOS8data]#cut -c 2-4 f3
234
BCD
[16:18:34root@CentOS8data]#cut -c -2 f3
12
AB

-nb的作用:防止切割多字节字符(可认为等效于-c)

#已知每个汉字大小为3byte!!!

  • 正常给定正确的byte

    [16:41:44root@CentOS8data]#cat f1
    我来烤烤你
    [16:41:49root@CentOS8data]#cut -b 1 f1    #一个字节,不足以显示出第一个汉字
    
    [16:43:21root@CentOS8data]#cut -b -3 f1   #每3个字节即可完整显示一个汉字
    我
    [16:43:23root@CentOS8data]#cut -b -6 f1
    我来
    [16:43:26root@CentOS8data]#cut -b -9 f1
    我来烤
    [16:43:28root@CentOS8data]#cut -b -12 f1
    我来烤烤
    [16:43:34root@CentOS8data]#cut -b -15 f1
    我来烤烤你
    
    [16:43:40root@CentOS8data]#cut -c -1 f1   #每个汉字一个字符,即可按个数显示
    我
    [16:43:58root@CentOS8data]#cut -c -2 f1
    我来
    [16:44:00root@CentOS8data]#cut -c -3 f1
    我来烤
    [16:44:02root@CentOS8data]#cut -c -4 f1
    我来烤烤
    [16:44:04root@CentOS8data]#cut -c -5 f1
    我来烤烤你
    
  • 不是3的整数倍,使用 -n 可以不分割汉字(多字节字符)

    [16:51:38root@CentOS8data]#cut -nb -1 f1
    我
    [16:51:54root@CentOS8data]#cut -nb -3 f1
    我来烤
    [16:51:57root@CentOS8data]#cut -nb -5 f1
    我来烤烤你
    

常用功能:-f(需要先用-d指定分隔符,不指定则为tab)

  • 不指定的情况

    [11:58:36root@CentOS8data]#cat f1 
    1 2   3
    4 5   6
    7 8   9
    [11:58:40root@CentOS8data]#cat -A f1   #确认分隔符为TAB
    1^I2^I3$
    4^I5^I6$
    7^I8^I9$
    [11:58:44root@CentOS8data]#cut -f 2 f1
    2
    5
    8
    [11:59:19root@CentOS8data]#cut -f 3 f1
    3
    6
    9
    [11:59:22root@CentOS8data]#cut -f 2-3 f1      #2-3表示范围
    2 3
    5 6
    8 9
    [11:59:27root@CentOS8data]#cut -f 1,3 f1
    1 3
    4 6
    7 9
    
  • 指定分隔符(取范围时,输出结果会包含分隔符!!!

    [16:23:43root@CentOS8data]#cat f1
    1#2#3#4#5#6#7#8#9
    A#B#C#D#E#F#G#H#I
    [16:30:52root@CentOS8data]#cut -d "#" -f 5 f1
    5
    E
    [16:24:46root@CentOS8data]#cut -d "#" -f -5 f1 (取范围,是1#2#3#4#5而不是12345)
    1#2#3#4#5
    A#B#C#D#E
    [16:31:00root@CentOS8data]#cut -d "#" -f 5- f1
    5#6#7#8#9
    E#F#G#H#I
    [16:31:09root@CentOS8data]#cut -d "#" -f 2-4 f1
    2#3#4
    B#C#D
    

其他功能:在输出结果中指定需要显示的分隔符

--output-delimiter=STRING指定输出分隔符

#以':'为分隔符取出文件f2的第2~3列,并使输出结果中的分隔符显示为=

[12:07:34root@CentOS8data]#cat f2
1:2:3
4:5:6
[12:07:39root@CentOS8data]#cut -d ":" -f 2,3 f2
2:3
5:6
[12:08:46root@CentOS8data]#cut -d ":" --output-delimiter="=" -f 2,3 f2
2=3
5=6

其他功能:取反 complement

#以:为分隔符,取出/etc/passwd文件中的第1,3,4,5列
[16:31:16root@CentOS8data]#cut -d : -f 1,3-5 /etc/passwd
root:0:0:root
bin:1:1:bin
daemon:2:2:daemon
adm:3:4:adm
......

#以:为分隔符,取出/etc/passwd文件中除了第1,3,4,5列之外的其他列
[16:39:40root@CentOS8data]#cut -d : -f 1,3-5 --complement /etc/passwd
x:/root:/bin/bash
x:/bin:/sbin/nologin
x:/sbin:/sbin/nologin
x:/var/adm:/sbin/nologin

2.1.5 合并文件paste

默认以行为单位,合并多个文件同行号的列到一行

合并文件的拼接处默认以TAB分隔

paste [OPTION]... [FILE]...
-d 指定分隔符,默认用TAB
-s 先把各文件所有行合成一行,再上下进行拼接 #N个文件合并结果为N行
[17:04:20root@CentOS8data]#cat f2
12441
574575
789789
[17:04:22root@CentOS8data]#cat f3
ABCD
QWER
ZXCV
[17:04:24root@CentOS8data]#paste f2 f3            #正常合并,同行拼接
12441   ABCD
574575  QWER
789789  ZXCV
[17:04:34root@CentOS8data]#paste -d @ f2 f3       #合并同时指定分隔符
12441@ABCD
574575@QWER
789789@ZXCV
[17:04:47root@CentOS8data]#paste -s f2 f3         #先换成一行再叠加合并
12441   574575  789789
ABCD    QWER    ZXCV
[17:05:24root@CentOS8data]#paste -s -d ':' f2 f3  #叠加与指定分隔符同时作用
12441:574575:789789
ABCD:QWER:ZXCV

2.2 文本分析工具(重点)

2.2.1 统计数据wc

对指定文本文件或STDIN的各项指标进行统计

包括:文件的行总数、单词总数、字节总数和字符总数

wc [opt] [file]
常用选项
-l 只计数行数
-w 只计数单词总数
-c 只计数字节总数
-m 只计数字符总数
-L 显示文件中#最长行的长度   即为文件宽度

不加选项时默认统计前三项

[11:30:17root@CentOS8data]#wc cat.log 
 28 173 946 cat.log
# 28行   173词   946字节

应用举例:

  • 统计登录用户的数量

    [11:34:09root@CentOS8data]#who | wc -l 
    2
    
  • 统计conf后缀文件的个数

    [11:36:11root@CentOS8data]#ls /etc/*.conf | wc -l
    41
    

2.2.2 排序整理sort

对文件的所有行进行排序

默认:数字在字母之前、字母排序不分大小写

#也可将每行切分成字段再进行排序
-t 指定切割字符
-k 指定以切割后第几段排序
-n 指定以数值大小排序
-r 倒序排列
-R 随机排列
-b 忽略每行行首的若干个空格字符
[20:34:55root@CentOS8/data]# cat jackB
abd
ABd
4Am
5be
Bcc
b56
Car
cvb
c4d
[20:35:00root@CentOS8/data]# sort jackB  #先数字再字母,字母相同再看第二列
4Am
5be
abd
ABd
b56
Bcc
c4d
Car
cvb
#将/etc/passwd文件以:分隔后的第三字段按数字排序,取前5行
[20:49:22root@CentOS8/data]# sort -t ":" -k3 -n /etc/passwd| head -n 5
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

2.2.3 去重处理uniq

以行为单位去除重复内容(只去除连续重复行)

-c 显示重复次数
-d 只显示连续重复行(重复但不连续的不算!!!)
-u 只显示不连续重复的行
[20:59:29root@CentOS8/data]# cat -n jackC
     1  111
     2  111
     3  111
     4  2345
     5  666
     6  666
     7  2345
     8  888
[20:59:43root@CentOS8/data]# uniq jackC       #去除连续重复的行
111
2345
666
2345
888
[21:00:03root@CentOS8/data]# uniq -c jackC    
      3 111      #连续重复3次
      1 2345
      2 666      #连续重复2次
      1 2345
      1 888
[21:01:04root@CentOS8/data]# uniq -d jackC    #出现过连续重复的行 的内容
111
666
[21:01:22root@CentOS8/data]# uniq -u jackC    #没出现过连续重复的行
2345
2345
888

2.2.4 异同比较diff

对比分析两个普通文件的不同之处(命令cmp比较两个二进制文件的区别)

[21:09:37root@CentOS8/data]# echo 2020-{01..12} | tr ' ' '\n' > jackA
[21:10:16root@CentOS8/data]# cat jackA
2020-01
2020-02
2020-03
2020-04
2020-05
2020-06
2020-07
2020-08
2020-09
2020-10
2020-11
2020-12
[21:10:20root@CentOS8/data]# echo 2020-{01..8} | tr ' ' '\n' > jackB
[21:10:46root@CentOS8/data]# cat jackB
2020-01
2020-02
2020-03
2020-04
2020-05
2020-06
2020-07
2020-08

[21:10:52root@CentOS8/data]# diff jackA jackB
9,12d8    #表示9~12行有不同
< 2020-09
< 2020-10
< 2020-11
< 2020-12

选项 -y可以并排对比 -W指定并排的间距

(#已手动修改了jackB两处内容)
[21:17:35root@CentOS8/data]# diff -y -W 30 jackA jackB
2020-01     2020-01
2020-02     2020-02
2020-03       | 2018-03   |表示不同之处
2020-04     2020-04
2020-05     2020-05
2020-06       | 2018-06
2020-07     2020-07
2020-08     2020-08
2020-09       <          < 表示后面文件少一行(同理 > 表示前面少)
2020-10       <
2020-11       <
2020-12       <

选项 -u 用来输出统一的(unified)diif格式文件(适合用于补丁文件)

[root@centos8/~]# diff -u jackA jackB
--- jackA   2020-08-05 19:17:03.738281873 +0800
+++ jackB   2020-08-05 19:17:57.517282028 +0800
@@ -1,12 +1,8 @@     #表示两个文件的行数
 2020-01
 2020-02
-2020-03  #不同之处
+2018-03  #不同之处
 2020-04
 2020-05
-2020-06  #不同之处
+2018-06  #不同之处
 2020-07
 2020-08
-2020-09  #不同之处
-2020-10
-2020-11
-2020-12

patch命令:利用diff比较结果输出的内容进行还原

[root@centos8/~]# diff -u jackA jackB > jack.patch  #保存补丁文件
[root@centos8/~]# rm -f jackB
[root@centos8/~]# patch -b jackA jack.patch 
patching file jackA
#表示用补丁恢复文件jackB,并覆盖在jackA之中   同时 -b将jackA内容备份到jackA.orig

[root@centos8/~]# cat jackA  (此即为jackB被恢复的内容)
2020-01
2020-02
2018-03
2020-04
2020-05
2018-06
2020-07
2020-08
[root@centos8/~]# cat jackB 
cat: jackB: No such file or directory
[root@centos8/~]# cat jackA.orig  (此即为jackA备份的内容)
2020-01
2020-02
2020-03
2020-04
2020-05
2020-06
2020-07
2020-08
2020-09
2020-10
2020-11
2020-12
[root@centos8/~]# 

cmp命令:比较二进制程序的源码不同之处

[root@centos8/~]# ll /usr/bin/dir /usr/bin/ls
-rwxr-xr-x. 1 root root 166448 May 12  2019 /usr/bin/dir
-rwxr-xr-x. 1 root root 166448 May 12  2019 /usr/bin/ls
[root@centos8/~]# ll /usr/bin/dir /usr/bin/ls -i
201508553 -rwxr-xr-x. 1 root root 166448 May 12  2019 /usr/bin/dir
201508574 -rwxr-xr-x. 1 root root 166448 May 12  2019 /usr/bin/ls
[root@centos8/~]# diff /usr/bin/dir /usr/bin/ls 
Binary files /usr/bin/dir and /usr/bin/ls differ
[root@centos8/~]# cmp /usr/bin/dir /usr/bin/ls 
/usr/bin/dir /usr/bin/ls differ: byte 737, line 2
[root@centos8/~]# hexdump -s 730 -Cn 7 /bin/dir
000002da  00 00 47 4e 55 00 b0                              |..GNU..|
000002e1
[root@centos8/~]# hexdump -s 730 -Cn 7 /bin/ls
000002da  00 00 47 4e 55 00 93                              |..GNU..|
000002e1
[root@centos8/~]# 

3 grep与正则表达式

3.0 grep命令

grep [op]... parttern [file]...
选项:
--color=auto #表示对匹配到的结果着色处理(默认开启)
[11:50:29root@CentOS8/~]# alias grep
alias grep='grep --color=auto'
[11:50:34root@CentOS8/~]# grep root /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

排除过滤 - v

grep默认只显示符合条件的行,- v 选项只显示不符合条件的内容

[11:52:17root@CentOS8/~]# grep -v root /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
.......

**忽略大小写- i **:gerp默认大小写敏感,- i 选项忽略大小写

[14:02:53root@CentOS8/~]# grep ROOT /etc/passwd    #不加选项无输出结果
[14:07:15root@CentOS8/~]# grep -i ROOT /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

显示行号 -n :文本过多时,显示行号方便查看
(显示
匹配结果在原文中的行号

[14:07:23root@CentOS8/~]# grep -n root /etc/passwd
1:root:x:0:0:root:/root:/bin/bash
10:operator:x:11:0:operator:/root:/sbin/nologin

**显示行数量 - c **, 只显示有多少行符合条件

[14:48:55root@CentOS8/~]# grep -c root /etc/passwd
2

- o 只显示keywords:- o 只显示所有匹配的内容,可知keywords数量

[15:05:16root@CentOS8/~]# grep -o root /etc/passwd
root
root
root
root

静默模式:- q 选项执行命令但不输出结果,常用于脚本条件判断

#执行后查看 $? 变量可知是否匹配。 $?=0表示有匹配结果
[15:07:09root@CentOS8/~]# grep aaaaa -q /etc/passwd
[15:11:52root@CentOS8/~]# echo $?
1   #表示无法匹配到'aaaaa'
[15:12:00root@CentOS8/~]# grep root -q /etc/passwd
[15:12:07root@CentOS8/~]# echo $?
0

ABC匹配结果附带前后行一起输出

-A 表示附带后面N行
-B 表示附带前面N行 
-C = '-A' + '-B'
#选项加上-n是为了证实确实是匹配结果的后面3行
[15:12:09root@CentOS8/~]# grep root -nA3 /etc/passwd
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
--
10:operator:x:11:0:operator:/root:/sbin/nologin
11-games:x:12:100:games:/usr/games:/sbin/nologin
12-ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
13-nobody:x:65534:65534:Kernel Overflow User:/:/sbin/nologin

选项 - e 多个条件满足其一几个条件就有几次 - e
特别地, - E 表示使用扩展正则表达式

  • 逻辑 或

    [15:17:15root@CentOS8/~]# grep -e root -e jack  /etc/passwd
    root:x:0:0:root:/root:/bin/bash
    operator:x:11:0:operator:/root:/sbin/nologin
    jacklee:x:1000:1000:jacklee:/home/jacklee:/bin/bash
    [15:19:46root@CentOS8/~]# grep -e root -e aaaa  /etc/passwd
    root:x:0:0:root:/root:/bin/bash
    operator:x:11:0:operator:/root:/sbin/nologin
    
    • 过滤规则过多,将其写在文件中使用选项 - f 进行匹配(逻辑 = 或)
      因为太多 - e 写起来太麻烦
    [15:34:59root@CentOS8/data]# cat a.txt 
    root
    body
    system
    [15:35:01root@CentOS8/data]# grep -f a.txt /etc/passwd
    root:x:0:0:root:/root:/bin/bash
    operator:x:11:0:operator:/root:/sbin/nologin
    nobody:x:65534:65534:Kernel Overflow User:/:/sbin/nologin
    systemd-coredump:x:999:997:systemd Core Dumper:/:/sbin/nologin
    systemd-resolve:x:193:193:systemd Resolver:/:/sbin/nologin
    
    • **选项 - f 另一个功能:用于取两文件的相同行****
    [root@centos8/~]# grep -f jackA jackA.orig 
    2020-01
    2020-02
    2020-04
    2020-05
    2020-07
    2020-08
    
  • 逻辑 与(使用管道而不是 - e )

    [15:26:01root@CentOS8/~]# grep root /etc/passwd | grep bash
    root:x:0:0:root:/root:/bin/bash
    [15:26:15root@CentOS8/~]# grep root /etc/passwd | grep aaaaa  #无结果
    [15:26:25root@CentOS8/~]#    
    

选项 - w 匹配单个单词
单词判断依据——字母数字下划线连续组合都视为单词
单词判断依据——字母数字下划线连续组合都视为单词
单词判断依据——字母数字下划线连续组合都视为单词

[15:26:25root@CentOS8/~]# grep  jack  /etc/passwd
jacklee:x:1000:1000:jacklee:/home/jacklee:/bin/bash    #包含jack有结果
[15:29:21root@CentOS8/~]# grep  -w jack  /etc/passwd   #指定jack无结果
[15:29:38root@CentOS8/~]# grep  -w jacklee  /etc/passwd
jacklee:x:1000:1000:jacklee:/home/jacklee:/bin/bash

3.1 基本正则表达式


  • 通配符用于匹配文件名
  • 正则表达式用于匹配复杂的文本内容,可理解为增强版的通配符功能

正则表达式把字符和次数分开表达
正则表达式把字符和次数分开表达
正则表达式把字符和次数分开表达
正则表达式把字符和次数分开表达

基本格式为引号括起来:

"PATTERN" 或 'PATTERN'

正则表达式的元字符按功能分四类,下面分别介绍

3.1.1字符串匹配

通配符 . 表示 . 字符本身,无特别意义
正则表达式 . 表示#任意单个字符(可以是汉字),为表示.需要转义为 \.
'使用( )分组也视为一个字符!!!!

[0-9] 或 [[:digit:]]   匹配单个数字

#以下3项是正则表达式特有!!!!!!!!!
[a-z] 或 [[:lower:]]   匹配单个小写字母
[A-Z] 或 [[:upper:]]   匹配单个大写字母
[a-zA-Z]或[[:alpha:]]  任意单个字母     #注意取反的格式:[^[:alpha:]]

[[:punct:]] 单个标点符号
[[:print:]] 单个可打印的所有字符
[[:graph:]] 单个可打印的非空白字符

[[:blank:]]   #空格和Tab
[[:space:]]   #任意单个空白字符(包括但不限于空格和Tab)

注意:
- [a-d]在通配符和正则表达式中的区别!!!
- [:lower:]是一个元字符,表示任意一个小写字母
  #在命令中,[[:lower:]]表示匹配任意一个小写字母,其他类似
- .表示任意单个字符,#可用 \.转义 表示.字符本身
- 可以混合使用,如[a-zA-Z]、[jack1-6]
  • 正则匹配单个字符

    [15:35:03root@CentOS8/data]# grep "r..t" /etc/passwd
    root:x:0:0:root:/root:/bin/bash      #本行的"root"符合"r..t"
    operator:x:11:0:operator:/root:/sbin/nologin
    ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin #本行的"r/ft"符合"r..t"
    
  • 正则中括号功能

    • 匹配指定字符集 特别注意 [a-d] = a b c d !!!

      #查看文件中的数字
      [15:52:03root@CentOS8/data]# grep "[0-9]" /etc/redhat-release 
      CentOS Linux release 8.0.1905 (Core)   #此处数字已经彩色显示
      
      #查看 ip a 输出的大写字母
      [16:04:45root@CentOS8/data]# ip a | grep "[[:upper:]]"
      1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue stateUNKNOWN group default qlen 1000
      2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
                #所有大写字母已经彩色显示
      
    • 中括号也可以任意指定某几个字符表示逻辑或

      [16:12:12root@CentOS8/data]# ls
      jackA  jackB  jackC  jackD
      [16:12:13root@CentOS8/data]# ls | grep "jack[AC]"
      jackA
      jackC
      [16:13:04root@CentOS8/data]# ls | grep "jack[^AC]"  #取反
      jackB
      jackD
      

3.1.2 匹配次数

指定某个内容出现的具体次数(keyword写在前)
只匹配前一个紧挨着的字符!!!!!!!!!!!!!!!!!!

*  表示匹配前面的字符# 0次或任意次  jack*表示字符k任意次,而不是jack!!!!
\? 表示匹配前面的字符# 0次或1次
\+ 表示匹配前面的字符# 至少1次

\{n\} 表示匹配前面的字符# 连续n次    与\{n,\}效果相同!!!!!!!!!!!
\{m,} 表示匹配前面的字符# 至少m次
\{,n\}表示匹配前面的字符# 最多n次
\{m,n\} 表示匹配前面的字符# m~n次
  • 0次、单次与任意次

    #下面"jacklee1*"表示匹配数字1而不是jacklee1 !!!!!!!!
    [16:39:05root@CentOS8/data]# grep "jacklee1*" jackA  #包括0次
    jacklee111
    jacklee2222
    jacklee33333
    [16:40:48root@CentOS8/data]# grep "jacklee1\?" jackA  #包括0次
    jacklee111
    jacklee2222
    jacklee33333
    [16:41:33root@CentOS8/data]# grep "jacklee1\+" jackA  #至少1次
    jacklee111
    
  • 匹配指定次数(特别注意,最多N次:包括0次

    [16:46:53root@CentOS8/data]# grep "1\{1\}" jackA 表示1出现1次即可
    jacklee111
    [16:48:09root@CentOS8/data]# grep "1\{3\}" jackA 表示连续3个1即可
    jacklee111
    [16:48:47root@CentOS8/data]# grep "1\{5\}" jackA 表示连续5个1即可
    [16:48:52root@CentOS8/data]#      原文件只有3个1,因此无匹配结果
    [16:48:52root@CentOS8/data]# grep "3\{5\}" jackA
    jacklee33333                      #原文件够5个3,因此有结果
    
    [16:55:49root@CentOS8/data]# grep "2\{2,4\}" jackA 表示2~4个2都可
    jacklee2222
    [16:57:32root@CentOS8/data]# grep "2\{2,6\}" jackA 表示2~6个2都可
    jacklee2222
    [16:57:38root@CentOS8/data]# grep "2\{5,6\}" jackA
    [16:57:49root@CentOS8/data]#      原文件只有4个2,因此5~6次无匹配结果
    
    [17:00:38root@CentOS8/data]# grep  "3\{2,\}" jackA 表示3至少2连击
    jacklee33333
    [17:00:41root@CentOS8/data]# grep  "3\{5,\}" jackA 表示3至少5连击
    jacklee33333
    [17:00:47root@CentOS8/data]# grep  "3\{6,\}" jackA
    [17:00:50root@CentOS8/data]#       至少6连击,因此无匹配结果
    
    [17:02:30root@CentOS8/data]# grep "1\{,2\}" jackA  '表示最多只需要两个1即可
    #表示最多2连击,因此2个1就已经足够匹配到
    jacklee111
    #同时,没有1也符合要求!!!因为0次也符合最多2次!!!
    jacklee2222
    jacklee33333
    
    [17:02:32root@CentOS8/data]# grep "1\{,4\}" jackA
    jacklee111
    jacklee2222
    jacklee33333
    
  • 匹配正负数字(负数的负号 - 需要特殊处理!!)

    [root@centos8/~]# echo -1 -2 223 -123 56 | egrep '-?[0-9]+'
    grep: invalid option -- '?'
    Usage: grep [OPTION]... PATTERN [FILE]...
    Try 'grep --help' for more information.
    
    #表示负数,需要转义负号!!! 3种办法均可
    [root@centos8/~]# echo -1 -2 223 -123 56 | egrep '\-?[0-9]+'
    -1 -2 223 -123 56
    [root@centos8/~]# echo -1 -2 223 -123 56 | egrep -- '-?[0-9]+'
    -1 -2 223 -123 56
    [root@centos8/~]# echo -1 -2 223 -123 56 | egrep '(-)?[0-9]+'
    -1 -2 223 -123 56
    [root@centos8/~]# 
    

3.1.3 位置锚定

在指定位置搜索字符串

 ^ 表示行首       
 $ 表示行尾
 
 \< 表示词首 = \bKEY
 \> 表示词尾 = KEY\b
 #单词的定义:数字字母下划线连续出现,无论多长都认为是一个单词
 
           ^$ 表示空行!!! #(无任何内容)
^[[:space:]]$ 表示空白行!!!#(只有不可见字符)

^PATTERN$    #匹配整行
\<PATTERN\>  #匹配整个单词
  • 整行范围匹配(行首 ^)

    #查找以root开头的行
    [17:25:09root@CentOS8/data]# grep "^root" /etc/passwd
    root:x:0:0:root:/root:/bin/bash
    
    #查找不以#开头的行!!!([^#]表示除了#)
    [17:41:42root@CentOS8/data]# cat /etc/fstab
    
    #
    # /etc/fstab
    # Created by anaconda on Tue Jul 28 20:19:00 2020
    #
    # Accessible filesystems, by reference, are maintained under '/dev/disk/'.
    # See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info.
    #
    # After editing this file, run 'systemctl daemon-reload' to update systemd
    # units generated from this file.
    #
    UUID=3d1ad34e-cc80-4047-9167-474bd4dfc5a5 /                       xfs     defaults        0 0
    UUID=ed944ea3-8ecd-43f6-af42-b1da086e778c /boot                   ext4    defaults        1 2
    UUID=8c8df88e-0a9e-494f-831f-8abffc065a4e /data                   xfs     defaults        0 0
    UUID=c47e4725-5b77-4d3f-8842-60e5689d45cc swap                    swap    defaults        0 0
    
    [17:41:59root@CentOS8/data]# grep "^[^#]" /etc/fstab  #
    UUID=3d1ad34e-cc80-4047-9167-474bd4dfc5a5 /                       xfs     defaults        0 0
    UUID=ed944ea3-8ecd-43f6-af42-b1da086e778c /boot                   ext4    defaults        1 2
    UUID=8c8df88e-0a9e-494f-831f-8abffc065a4e /data                   xfs     defaults        0 0
    UUID=c47e4725-5b77-4d3f-8842-60e5689d45cc swap                    swap    defaults        0 0
    
    [17:42:22root@CentOS8/data]# grep "^[^#].*" /etc/fstab  #
    UUID=3d1ad34e-cc80-4047-9167-474bd4dfc5a5 /                       xfs     defaults        0 0
    UUID=ed944ea3-8ecd-43f6-af42-b1da086e778c /boot                   ext4    defaults        1 2
    UUID=8c8df88e-0a9e-494f-831f-8abffc065a4e /data                   xfs     defaults        0 0
    UUID=c47e4725-5b77-4d3f-8842-60e5689d45cc swap                    swap    defaults        0 0
    
    "^[^#]"与"^[^#].*"最终结果相同,可认为等效
    

  • 整行范围匹配(行尾 $)

    #查找以bash结尾的行
    [18:01:17root@CentOS8/data]# grep "bash$" /etc/passwd
    root:x:0:0:root:/root:/bin/bash
    jacklee:x:1000:1000:jacklee:/home/jacklee:/bin/bash
    
  • 全行匹配(^ $)

    [18:04:11root@CentOS8/data]# cat jackB
    AAAA
    
    CCCCCCCC
    DDD
    EEEEEEE
    [18:04:23root@CentOS8/data]# grep -n "^D$" jackB     #全行匹配精确=D才符合条件
    [18:05:04root@CentOS8/data]# grep -n "^DDD$" jackB   #精确=DDD,匹配成功
    4:DDD
    [18:05:10root@CentOS8/data]# grep -n "^$" jackB      #匹配空行,第2行匹配成功
    2:
    [18:05:15root@CentOS8/data]# 
    
  • 基于单词进行锚定(\>或者\<)或者\b

    [19:12:33root@CentOS8/data]# cat -n jackC
         1    jack222  nnnn
         2    jack555  nnnnn
         3    jack888  mmmm
         4    aaab888  kkkk
         5    cccd888  rrrr
    [19:13:49root@CentOS8/data]# grep -n "\<jac" jackC
    1:jack222  nnnn
    2:jack555  nnnnn
    3:jack888  mmmm
    [19:13:54root@CentOS8/data]# grep -n "\bjac" jackC  \b写在前面表示词首
    1:jack222  nnnn
    2:jack555  nnnnn
    3:jack888  mmmm
    [19:13:59root@CentOS8/data]# grep -n "8\>" jackC
    3:jack888  mmmm
    4:aaab888  kkkk
    5:cccd888  rrrr
    [19:14:11root@CentOS8/data]# grep -n "8\b" jackC    \b写在后面表示词尾
    3:jack888  mmmm
    4:aaab888  kkkk
    5:cccd888  rrrr
    

3.1.4 分组和逻辑处理

分组:将多个字符 作为整体 来进行处理

正则表达式#分组的元字符:\(PATTERN\)

普通分组用法:

#查看包含(lee连续重复3次)的行
[19:25:27root@CentOS8/data]# cat jackA
jackjackjack
leeleelee
sjhdvbasjbvj
asjbfbaffnvjsn
[19:25:38root@CentOS8/data]# grep "\(lee\)\{3\}" jackA
leeleelee

后向引用:使用分组之后,后续再次引用只需要使用 \1、\2 等表示第一个、第二个分组
最适合用于vim搜索替换(形如 r..t 替换成 r..ter 的情况)



逻辑 或
除了使用选项 - e 还有符号 \| (其中\用来转义)

[19:30:00root@CentOS8/data]# cat jackC
cat
C
tomCat
Tom
#显示包含C或cat的行
[19:30:07root@CentOS8/data]# grep "C\|cat" jackC
cat
C
tomCat
#显示包含Cat或cat的行!!!!(组合用法)
[19:30:45root@CentOS8/data]# grep "\(c\|C\)at" jackC
cat
tomCat
[19:31:52root@CentOS8/data]# grep -e "cat" -e "Cat" jackC
cat
tomCat

@易错点&易混淆点

  • 在ERE中,除了后向引用 \1 \2 写法,其余所有斜线都可省略,若再加斜线,表示匹配字符本身

  • 中括号 [ ] 表示字符集,输入多个字符表示逻辑或

     正则表达式中 . 表示任意单个字符,可以是汉字
     [.] 表示 . 字符本身
    
  • 用kuozhan正则表达式表示IP地址的优化思路

    #利用次数锚定
    [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}
    '显然过于重复,而且首尾无法严格表示3位数,如1111.0.0.8  10.0.0.111abc均可被匹配到
    
    #利用次数锚定和位置锚定
    \<[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\>
    '可严格锁定IP格式,但是过于重复
    
    #再利用分组进行优化
    \<([0-9]{1,3}\.){3}[0-9]{1,3}\>
    
    [root@centos8/~]# ip a|egrep -o "\<([0-9]{1,3}\.){3}[0-9]{1,3}\>"
    127.0.0.1
    10.0.0.8
    10.0.0.255
    
    #此时仍然无法过滤255以上的数字!!!
    
    
  • gerp命令的 -f 选项,两种完全不同的功能

    grep -f a.txt /etc/passwd  #表示将a.txt中的所有内容作为条件进行 逻辑或 匹配
    
    grep -f jackA jackA.orig   #输出两文件内容相同的行
    
  • 位置锚定经典用法:

    ^[^#] 表示非#开头的行(同时也能排除空行)
    ^[^$#] = ^[^$]或^[^#] 表示除了空行和#开头行 (非$# = 非$或非#)
    
    即 ^[^#] 与 ^[^$#] 效果相同
    
  • vim搜索替换中也可先使用位置锚定确定在哪一行进行操作

    #在vim扩展命令模式输入如下内容:
    /位置锚定/s/pattren/替换内容/[修饰符]
    
  • a|b表示a或b,a|bcd表示a或bcd,(a|b)cd表示acd或bcd

  • [[:blank:]]与[[:space:]]的区别?
    blank表示空格和Tab
    space包括所有空格和制表符
    
  • 逻辑或

      - 选项 - e  (几个条件写几次)
      - 选项 - f  (多个条件写在文本中)
      - 字符 / |  (用于两个分组差距很小的情况)
    

3.2 扩展正则表达式

为了在复杂规则中更加直观,省略了转义符 \

egrep 或者 grep -E  启用扩展RE
? 表示匹配前面的字符    # 0次或1次
+ 表示匹配前面的字符    # 至少1次
{n}   表示匹配前面的字符# 连续n次
{m,n} 表示匹配前面的字符# m~n次
( )   表示分组
 |    表示逻辑 或

#显示包含Cat或cat的行
[19:57:16root@CentOS8/data]# cat jackC
cat
C
tomCat
Tom
[19:57:53root@CentOS8/data]# egrep "(c|C)at" jackC
cat
tomCat
[19:57:57root@CentOS8/data]# grep -E "(c|C)at" jackC
cat
tomCat

3.3 三剑客之sed

3.3.0 易错点

  • 地址与命令都不是必须的!

    [root@centos8 ~]# sed ''  表示script内容为空,接收标准输入并echo输出
    ddd
    ddd
    dddddddd
    dddddddd
    
    '不给地址:对全文进行处理
    '不输入命令:表示只匹配行,不进行其他操作
    
  • 模式空间内容 如何输出!!!!!!!!!(关键点)

    -n 是选项,表示不管是否匹配到,都不输出内容(否则会逐行输出模式空间的内容)
     p 是命令,表示打印'被匹配到的每一行
     d 是命令,表示删除在模式空间匹配到的行(用于排除)
     
     ' -n 与 p 搭配的意义:只显示匹配到的行,或 只显示编辑过的行
     如果都不写 :输出全文,对应行已经被编辑
     如果只有 -n:不输出任何内容
     如果只有 p :输出全文,而且被编辑的行输出两次
    
  • 使用script命令 d 取反时,在模式空间匹配到的行会被删除,未被匹配到的行会输出,达到了排除的效果
    此时不再需要选项 -n,否则不会输出任何内容

  • 地址范围 多行的意义(表示范围而不是逻辑或!!!)

     /pat1/,/pat2/  从pat1匹配到的行 ~ pat2匹配到的行
     #,/pat/        从第#行 ~ pat匹配到的行
    
  • 注意两者区别:

    [root@centos8 ~]# sed -n '1,4p' /etc/passwd
    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
    [root@centos8 ~]# sed -n '1p;4p' /etc/passwd
    root:x:0:0:root:/root:/bin/bash
    adm:x:3:4:adm:/var/adm:/sbin/nologin
    
  • Script命令在一次操作中只能出现一种(比如 p 与 = 不能同时使用)

  • 命令 a \, i \ ,c \ 上下插入行或替换本行

    其中斜线可省略
    a \txt 可写为 a txt 表示在下一行插入新行,内容为txt
    
  • 地址和s///中的pattern默认都不支持ERE,需要启用选项 -r 或 -E

3.3.1 工作原理

sed 即 Stream EDitor,和 vi 不同,sed是行编辑器

Sed是从文件或管道中读取一行,处理一行,输出一行;再读取一行,再处理一行
每当处理一行时,把当前处理的行存储在临时缓冲区中,称为模式空间pattern space
一次处理一行的设计模式使得sed性能很高,sed在读取大文件时不会出现卡顿的现象

3.3.2 基本用法

sed [option]... 'script;script;...' inputfile...

其中"script" = /地址/动作
可以同时进行多个script操作,用 ; 隔开(如'2p;5p')

-n   不输出模式空间内容到屏幕,即不自动打印(核心选项)
-e   多点编辑(逻辑或)
-r/E 使用扩展正则表达式(扩展)
-i.bak  先备份文件并编辑源文件
-f /PATH/SCRIPT_FILE 从指定文件中读取编辑脚本

script地址

1.不给地址:对全文进行处理

2.单地址:
 #:指定的行
 $:最后一行
 /pattern/:被此处模式所能够匹配到的每一行(正则)
 
3.范围地址:
 #, #
 #,+#
 /pat1/,/pat2/  从pat1匹配到的行 ~ pat2匹配到的行
 #,/pat/        从第#行 ~ pat匹配到的行
 
4. 步进:~
  1~2 奇数行
  2~2 偶数行

script命令(只能单独使用)

不输入命令:表示只匹配行,不进行其他操作

p 打印当前模式空间内容,追加到默认输出之后
d 删除模式空间匹配的行,并立即启用下一轮循环(取反)

a [\]text 在指定行下一行插入文本行,支持使用\n实现多行追加
i [\]text 在指定行上一行插入文本行
c [\]text 替换行为单行或多行文本

w /path/file 保存模式匹配的行至指定文件
r /path/file 读取指定文件的文本至模式空间中匹配到的行后

= 只显示匹配到的行号
! 模式空间中匹配行取反处理

s/pattern/string/修饰符 
查找替换,支持使用其它分隔符,可以是其它形式:s@@@,s###
    替换修饰符:
    g 行内全局替换
    p 显示替换成功的行
    w /PATH/FILE 将替换成功的行保存至文件中

1范例:可接受标准输入

[root@centos8 ~]# sed ''  表示script内容为空
ddd
ddd
dddddddd
dddddddd
fffffffffffffffffff
fffffffffffffffffff     #使用ctrl+D退出
[root@centos8 ~]# 

2范例:打印内容次数

#sed默认为echo一次,'p'再次打印,-n不打印
[root@centos8 ~]# sed '' /etc/issue
\S
Kernel \r on an \m

[root@centos8 ~]# sed -n '' /etc/issue
[root@centos8 ~]# sed  'p' /etc/issue
\S
\S
Kernel \r on an \m
Kernel \r on an \m


[root@centos8 ~]# sed -n 'p' /etc/issue   #加一减一还是默认ehco
\S
Kernel \r on an \m

[root@centos8 ~]# 

3范例:指定打印第几行

[root@centos8 ~]# sed -n '1p' /etc/passwd
root:x:0:0:root:/root:/bin/bash
[root@centos8 ~]# sed -n '2p' /etc/passwd
bin:x:1:1:bin:/bin:/sbin/nologin
[root@centos8 ~]# sed -n '3p' /etc/passwd
daemon:x:2:2:daemon:/sbin:/sbin/nologin

[root@centos8 ~]# ip a | sed -n '9p' 
    inet 10.0.0.8/24 brd 10.0.0.255 scope global noprefixroute ens160

4范例:使用/pattern/单地址匹配

[root@centos8 ~]# ip a | sed -n '/inet/p'     #此处 p 是script命令!!
    inet 127.0.0.1/8 scope host lo
    inet6 ::1/128 scope host 
    inet 10.0.0.8/24 brd 10.0.0.255 scope global noprefixroute ens160
    inet6 fe80::e016:ece6:7e36:e2ec/64 scope link noprefixroute 
    
[root@centos8 ~]# df|sed -n '/^\/dev\/sd/p'   #此处 p 是script命令!!
/dev/sda2      104806400 3728680 101077720   4% /
/dev/sda5       52403200  398892  52004308   1% /data
/dev/sda1         999320  106688    823820  12% /boot

5范例:取地址范围

[root@centos8 ~]# seq 9 | sed -n '2,5p'
2
3
4
5
[root@centos8 ~]# seq 9 | sed -n '2,+2p'
2
3
4
[root@centos8 ~]# seq 9 | sed -n '7,$p'
7
8
9

6范例:步进取行

[root@centos8 ~]# seq 8 | sed -n '1~2p'
1
3
5
7
[root@centos8 ~]# seq 8 | sed -n '2~2p'
2
4
6
8

#使用命令 d 表示删除匹配结果,输出取反!!!(不能再加选项 -n)
[root@centos8 ~]# seq 6 | sed -n '1~2d'
[root@centos8 ~]# seq 8 | sed -n '1~2d'  因为d已经删除模式空间内容,再加-n就删除了原本的echo
[root@centos8 ~]# seq 8 | sed  '1~2d'
2
4
6
8
[root@centos8 ~]# seq 8 | sed  '2~2d'
1
3
5
7

7范例:多点编辑(两种方式)

[root@centos8 ~]# seq 8 | sed -e '3d' -e '5d' -e '7d'
1
2
4
6
8
[root@centos8 ~]# seq 8 | sed '1d;2d;5d'
3
4
6
7
8

8范例:编辑前备份(注意格式,-i 后面直接紧挨着指定后缀)

[root@centos8 ~]# cat seq.log 
1
2
3
4
5
6
[root@centos8 ~]# sed -i.bak '2d;4d;5d' seq.log
[root@centos8 ~]# ll se*
-rw-r--r--. 1 root root  6 Aug 11 11:45 seq.log
-rw-r--r--. 1 root root 12 Aug 11 11:44 seq.log.bak  #备份成功
[root@centos8 ~]# cat seq.log.bak 
1
2
3
4
5
6
[root@centos8 ~]# cat seq.log
1
3
6

9范例:

#只显示非#开头的行
[root@centos8 ~]# sed -n '/^#/!p' /etc/fstab 

UUID=3d1ad34e-cc80-4047-9167-474bd4dfc5a5 /                       xfs     defaults        0 0
UUID=ed944ea3-8ecd-43f6-af42-b1da086e778c /boot                   ext4    defaults        1 2
UUID=8c8df88e-0a9e-494f-831f-8abffc065a4e /data                   xfs     defaults        0 0
UUID=c47e4725-5b77-4d3f-8842-60e5689d45cc swap                    swap    defaults        0 0

#删除所有以#开头的行
[root@centos8 ~]# sed '/^#/d' fstab 

UUID=3d1ad34e-cc80-4047-9167-474bd4dfc5a5 /                       xfs     defaults        0 0
UUID=ed944ea3-8ecd-43f6-af42-b1da086e778c /boot                   ext4    defaults        1 2
UUID=8c8df88e-0a9e-494f-831f-8abffc065a4e /data                   xfs     defaults        0 0
UUID=c47e4725-5b77-4d3f-8842-60e5689d45cc swap                    swap    defaults        0 0

10范例:在匹配到的行上下插入新行,或替换之

sed'/root/a\superman' /etc/passwd   在匹配行的上一行插入新一行,内容为superman
sed '/root/i\superman' /etc/passwd  在匹配行的下一行插入新一行,内容为superman
sed '/root/c\superman' /etc/passwd  将匹配到的行内容替换为superman

11范例:批量删除指定行

sed '/^$/d' file   删除所有空行
sed '1,10d'  file  删除1~10行

12范例:(nl命令对文本逻辑排序,显示行号)

[root@centos8 ~]# nl /etc/passwd|sed '2a \tea'
     1  root:x:0:0:root:/root:/bin/bash
     2  bin:x:1:1:bin:/bin:/sbin/nologin
tea
     3  daemon:x:2:2:daemon:/sbin:/sbin/nologin
     4  adm:x:3:4:adm:/var/adm:/sbin/nologin
..........

[root@centos8 ~]# nl /etc/passwd|sed '2c tea'   #省略斜线也可
     1  root:x:0:0:root:/root:/bin/bash
tea
     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
..........

搜索替换 s / / /

s/pattern/string/修饰符 
查找替换,支持使用其它分隔符,可以是其它形式:s@@@,s###
    替换修饰符:
    g 行内全局替换
    p 显示替换成功的行     
    #注意区别:命令p表示打印模式空间内容,此处s///占用了原来的p的位置和功能,因此作为补充,仍然是p
    w /PATH/FILE 将替换成功的行保存至文件中
sed 's/test/mytest/g' example    #也可表示为's/test/my&/'(而且是全局替换)
#将文件example全部内容中的test替换为mytest

以下两者每个匹配行只处理第一个root,全局处理需要再加g
sed -n 's/root/&superman/p' /etc/passwd 
#表示对有root的行 的第一个root 后面追加superman
sed -n 's/root/superman&/p' /etc/passwd 
#表示对有root的行 的第一个root 前面追加superman

sed -e 's/dog/cat/g' -e 's/hi/lo/g' pets 
#将文件 pets 中的所有dog换为cat ,所有hi换为lo ,并输出编辑后的全文

sed -i.bak  's/dog/cat/g' pets
#将文件 pets 备份为pets.bak,且所有dog换为cat,并输出编辑后的全文

正则表达式的运用:

从命令 ip a 取IP地址:

'至少一个非数字,接(一个以上数字或.连续出现),接任意内容
[root@centos8 ~]#ip a | sed -nr "9s/[^0-9]+([0-9.]+).*/\1/p"
10.0.0.8

'以至少一个非数字开头,接(一个以上数字或.连续出现),接任意内容   此方法可简化为1
[root@centos8 ~]#ip a | sed -nr '9s/^[^0-9]+([0-9.]+).*$/\1/p'
10.0.0.8

最佳:'至少一个非数字,接(连续出现的7~15个数字或.),接任意内容   
[root@centos8 ~]#ip a | sed -nr '9s/[^0-9]+([0-9.]{7,15}).*/\1/p' 
10.0.0.8

'利用具体内容前后夹逼
[root@centos8 ~]#ip a | sed -nr '9s/(.*inet )([0-9].*)(\/.*)/\2/p' 
10.0.0.8

'使用两次 s///的空白替换(删除)功能,前后夹逼删除
[root@centos8 ~]#ip a | sed -n '9s/.*inet //;9s/\/.*//p'    #不必句首句尾锚定
10.0.0.8 
[root@centos8 ~]#ip a | sed -n '9s/.*inet //p' | sed -n 's/\/.*//p' #管道后只剩一行,不需9s
10.0.0.8 

取dirname和basename

'关键: [^/]+  至少一个不是 / 开头的字符(从左往右抓)
[root@centos8 ~]# echo /etc/sysconfig/network-scripts/ | sed -nr 's#(^/.*/)([^/]+/?)#\1#p'
/etc/sysconfig/
[root@centos8 ~]# echo /etc/sysconfig/network-scripts/ | sed -nr 's#(^/.*/)([^/]+/?)#\2#p'
network-scripts/

取文件的前缀和后缀

'关键:后缀中没有 字符.
[root@centos8 ~]# echo a.c-d.345.gz|sed -nr 's/(.+)\.([^.]+)/\1/p'
a.c-d.345
[root@centos8 ~]# echo a.c-d.345.gz|sed -nr 's/(.+)\.([^.]+)/\2/p'
gz

'使用grep
[root@centos8 ~]# echo a.c-d.345.gz|grep -Eo '.*\.'
a.c-d.345.  #不够完美
[root@centos8 ~]# echo a.c-d.345.gz|grep -Eo '[^.]+$'
gz

范例:去掉所有注释和空行

sed  -r  '/^(#|$)/d'
sed  -r  '/^#|^$/d' 

范例:引用变量注意不能使用单引号

首先解释语法:
[root@centos8 ~]#  sed "s/^/$RANDOM.rmvb/"  表示在stdin的前面添加"$RANDOM.rmvb"
AAAAAA
3272.rmvbAAAAAA

[root@centos8 ~]# echo | sed "s/^/$RANDOM.rmvb/"
30932.rmvb
[root@centos8 ~]# echo | sed 's/^/$RANDOM.rmvb/'
$RANDOM.rmvb
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352