文本处理三剑客之sed

sed:stream editor 流编辑器,对标准输出或文件逐行进行处理

sed 是从文件或管道中读取一行,处理一行,输出一行;再读取一行,再处理一行,再输出一行,直到最后一行。每当处理一行时,把当前处理的行存储在临时缓冲区中,称为**模式空间(PatternSpace)**,接着用 sed 命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。一次处理一行的设计模式使得 sed 性能很高,sed 在读取大文件时不会出现卡顿的现象。如果使用 vi 命令打开几十 M 上百 M 的文件,明显会出现有卡顿的现象,这是因为 vi 命令打开文件是一次性将文件加载到内存,然后再打开。Sed 就避免了这种情况,一行一行的处理,打开速度非常快,执行速度也很快

1
sed [OPTION]... 'script;script;...' [input-file]...

基本用法

OPTION

1
2
3
4
5
6
-n         # 不输出模式空间内容到屏幕,即不自动打印,如果不加-n,会自动打印每一行
-e # 当script有多个时,使用-e,多个script之间是或关系,$(-e 'script1' -e 'script2') 等同于 $('script1;script2')
-f file # 从指定文件中读取script
-r,-E # 支持扩展正则表达式
-i # 对文件直接进行修改,而不是输出到终端
-i.bak # 先备份原文件再修改文件

script

script 是 地址命令 的组合,地址用来定位,命令用来对定位的内容进行操作

地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
不给地址:对全文进行处理

单地址:
n:n是阿拉伯数数字,指定行
$:最后一行
/pattern/:匹配到的每一行

地址范围:
n,n:第n行到第n行 3,6 从第3行到第6行
n,+n:第n行开始,往后再匹配n行 3,+4 从第3行到第7行
/pattern1/,/pattern2/:从pattern1匹配到行,到pattern2匹配到的行,再往下继续,从pattern1匹配到行,到pattern2匹配到的行...

步进:~

first\~step:匹配first行,然后步进为step向后继续匹配,例如 1\~2 就是匹配奇数行、2\~2就是匹配偶数行

命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
p         # print 打印查询的行
Ip # 忽略大小写输出
d # 删除匹配到的行
a # append,追加内容到下一行,支持使用\n实现多行追加; 如果要解析空格,需要用反斜杠\
i text # insert,在行前面插入内容
c text # change,替换行
w file # 保存模式匹配的行到指定文件
r file # 从file中读取内容,插入到匹配到的行的下一行
= # 打印匹配到行的行号
! 命令 # 对没有匹配到的行进行处理,例如 !p 是打印未匹配到的行、!atext 是在未匹配到的行的下一行添加text

s/pattern/string/修饰符 # 查找替换
修饰符:
g # 一行里如果由多个匹配到的内容,全部替换。默认只替换第一个。
p # 打印替换成功的行
w file # 将成功替换的行保存至文件中
I,i # 忽略大小写

练习

1.将 php.ini-production 中的无关信息去掉

1
sed -E '/^; /d;/^;+;$/d' /usr/local/src/php-7.4.8/php.ini-production | uniq > php.ini

2.获取分区利用率

1
lujinkai@Z510:~$ df | sed -En '/^\/dev\/sd/s/.* ([0-9]+)%.*/\1/p'

3.取基名和目录名

1
2
3
# 使用/作为分隔符,如果搜索条件中出现/需要转义,所以为了易读性,不建议使用搜索条件中出现的字符作为分割符
pwd |sed -r 's/(^\/.*\/)([^\/]+\/?)/\2/' # 取基名
pwd |sed -r 's@(^/.*/)([^/]+/?)@\2@' # 取目录

4.将非#开头的行加#

1
2
3
4
5
lujinkai@Z510:~/data/test$ sed -Ei 's/^[^#].*/# &/' a.log
# 或
lujinkai@Z510:~/data/test$ sed -Ei 's/(^[^#].*)/# \1/' a.log
# 或
lujinkai@Z510:~/data/test$ sed -ri '/^#/!s/^/# /' a.log

5.将#开头的行删除#

1
lujinkai@Z510:~/data/test$ sed -ri 's/^#(.*)/\1/' a.log

高级用法

略。。。