sed - 文本分析与转换工具 (1) 入门

  • 原创
  • Madman
  • /
  • 2018-04-09 09:10
  • /
  • 0
  • 225 次阅读

Sed - 文本分析与转换工具 (1) 入门-min.png

Synopsis: sed(意为流编辑器,源自英语“stream editor”的缩写)是类Unix系统中用来在一个输入流(文件或者管道中的输入)执行基本的文本过滤和转换的工具,它是非交互式的(不同于vim),同时又是面向字符流的,输入的字符流经过一个或多个有顺序的sed操作指令处理后,输出到stdout或与输入文件不同的其它文件中。你可以使用sed在不打开文件的情况下,从指定位置处开始编辑、文本替换、删除或选择性输出文本内容

sed系列:


1. 初步认识

GNU sed文档

Sed Tutorial

sed命令的基本格式为sed [options] SCRIPT INPUTFILE...,默认情况下sed会依次处理输入文件的每一行,除非限定了地址范围(还是会读取每一行,但只对匹配的行应用操作命令)。

SCRIPT至少要包括一个操作指令,不然sed只会原样输出文件内容到屏幕那样没有意义,而每条操作指令instruction都由地址addresses和命令commands组成(建议每条指令都用引号引起来,这样可避免shell对诸如空格、$等特殊字符的处理,此规则同样适应于grep和awk),只有地址或地址范围匹配成功,才会执行后面的命令。多条操作指令用;隔开或者使用-e选项每次指定一条操作指令。操作指令中的命令如果有多个,需要用{}包括起来,同时命令之间用;隔开。多个地址可以使用{}嵌套(即多重匹配条件)。

1. 原样输出
# sed '' /etc/passwd

2. 只打印最后一行,-n是选项,默认情况下sed会依次输出每一行到stdout,-n选项会禁止sed输出。'$ p'整体是操作指令,其中$是单个地址,表示文件最后一行;p是命令,表示打印pattern space中的内容到stdout。所以整行sed命令就是先用-n禁止sed输出,再用'$ p'只输出指定行的内容
# sed -n '$ p' /etc/passwd

3. 如果省略地址,则命令将应用于文件的每一行上
# sed -n 'p' /etc/passwd

4. 必须指定命令,否则sed会报错
# sed -n '1,5' /etc/passwd
sed: -e expression #1, char 3: missing command

5. 多个操作指令,以冒号分隔多条指令,外面再用大括号包括起来
# sed -n '{ 1 p ; /mail/ p }' /etc/passwd
或者使用-e选项
# sed -n -e '1 p' -e '/mail/ p' /etc/passwd

6. 一个操作指令中有多个命令,先输出行号再打印行内容
# sed -n '/nologin/ { = ; p }' /etc/passwd

7. 多个地址嵌套,第1至10行中,包含nologin字串的行,先输出行号再打印行内容
# sed -n '1,10 { /nologin/ { = ; p } }' /etc/passwd

如果没有指定-e-f选项,那么第一个非选项参数被视为SCRIPT,其它非选项参数被视为输入文件。下面的命令是等价的,都是把字符串hello替换为world :

# sed 's/hello/world/' input.txt > output.txt

# sed -e 's/hello/world/' input.txt > output.txt
# sed --expression='s/hello/world/' input.txt > output.txt

# echo 's/hello/world/' > myscript.sed
# sed -f myscript.sed input.txt > output.txt
# sed --file=myscript.sed input.txt > output.txt

如果没有指定输入文件或者用-代表INPUTFILE,sed都会从标准输入stdin读取数据

# sed 's/hello/world/' input.txt > output.txt
# sed 's/hello/world/' < input.txt > output.txt
# cat input.txt | sed 's/hello/world/' - > output.txt

2. 处理流程

sed的工作流可以归结为读取、执行操作指令、输出三个步骤

说明:

  • READ : sed从输入流(文件或者管道中的输入)中读取一行内容,删除尾部的换行符,并把它缓存到一个被称为pattern space的空间内,接下来的所有操作只会影响缓存区的内容,所以默认情况下,sed并不会修改输入文件的内容,可以添加-i选项以in-a-place方式直接修改输入文件
  • EXECUTE: 执行第一条操作指令,判断pattern space空间的内容是否与指令的模式匹配,如果匹配则执行该指令中的命令,完成后,再取下一条操作指令,此时pattern space空间的内容已经被修改,下一条指令是作用于上一条指令修改后的内容上的;如果不匹配,则直接取下一条操作指令,继续判断是否匹配。注意,操作指令是按顺序作用于pattern space中的内容上的,所以操作指令的顺序很重要
  • DISPLAY: 当所有操作指令都被判断执行与否后,如果没有指定-n选项,默认情况下sed将会把修改后的pattern space内容输出到stdout,之后清空pattern space

3. 常用选项

3.1 基本选项

(1) -n, --quiet, --silent

默认情况下,pattern space中的内容在处理完成后将会打印到标准输出,该选项用于阻止该行为

# sed '' /etc/passwd

# sed -n '' /etc/passwd

(2) -e script, --expression=script

该选项指明后面是操作指令,如果sed命令的SCRIPT只有一个操作指令时,可以省略-e选项。多个操作指令时,每个指令前添加-e选项

1. 省略-e选项
# sed -n '1 p' /etc/passwd

2. 多个操作指令
# sed -n -e '1 p' -e '/mail/ p' /etc/passwd

3. 多个操作指令的另一种写法,以冒号分隔多条指令,外面再用大括号包括起来
# sed -n '{ 1 p ; /mail/ p }' /etc/passwd

4. 如果多个操作指令是每行书写时,可以省略冒号和大括号
# sed -n '
> 1p
> /mail/p
> ' /etc/passwd

(3) -f script-file, --file=script-file

指定包含要执行的操作指令的脚本文件

# echo -e "1 p\n/mail/ p" > myscript.sed
# sed -n -f myscript.sed /etc/passwd

3.2 GNU sed特有选项

(1) -i[SUFFIX], --in-place[=SUFFIX]

sed默认不会将操作指令的结果应用到文件内容上,只是修改pattern space空间的内容,即不会修改文件内容。-i选项会直接修改文件内容,如果指定了SUFFIX,则会先备份原文件,再修改文件内容

1. 测试文件的原始内容
# cat books.txt
1) A Storm of Swords, George R. R. Martin, 1216 
2) The Two Towers, J. R. R. Tolkien, 352 
3) The Alchemist, Paulo Coelho, 197 
4) The Fellowship of the Ring, J. R. R. Tolkien, 432 
5) The Pilgrimage, Paulo Coelho, 288 
6) A Game of Thrones, George R. R. Martin, 864

2. sed命令删除1至5行,屏幕上只会输出第6行
# sed '1,5 d' books.txt
6) A Game of Thrones, George R. R. Martin, 864

3. 再次查看测试文件的内容,没有改变
# cat books.txt
1) A Storm of Swords, George R. R. Martin, 1216 
2) The Two Towers, J. R. R. Tolkien, 352 
3) The Alchemist, Paulo Coelho, 197 
4) The Fellowship of the Ring, J. R. R. Tolkien, 432 
5) The Pilgrimage, Paulo Coelho, 288 
6) A Game of Thrones, George R. R. Martin, 864

4. 指定-i.bak,先备份再修改
# sed -i.bak '1,5 d' books.txt
# cat books.txt
6) A Game of Thrones, George R. R. Martin, 864
# cat books.txt.bak 
1) A Storm of Swords, George R. R. Martin, 1216 
2) The Two Towers, J. R. R. Tolkien, 352 
3) The Alchemist, Paulo Coelho, 197 
4) The Fellowship of the Ring, J. R. R. Tolkien, 432 
5) The Pilgrimage, Paulo Coelho, 288 
6) A Game of Thrones, George R. R. Martin, 864

(2) -r, --regexp-extended

默认使用BRE基本正则表达式,指定-r选项后使用ERE扩展正则表达式

4. 地址范围

地址是用来限定sed的命令将会作用于哪些行上,可以是行号正则表达式(用斜杠包裹 /regex/ )或特殊符号$(表示最后一行)等,addresses可以包含0个、1个或2个地址,用逗号分隔。

4.1 不指定地址

默认情况下,如果不指定地址范围,则sed会依次读入一行并处理

1. 打印所有行
# sed -n 'p' books.txt

4.2 指定一个地址

只会判断是否与该行匹配

1. 只打印第1行
# sed -n '1 p' books.txt

2. 只打印包含mail字符串的所有行
# sed -n '/mail/ p' books.txt

3. 特殊行符号$表示最后一行
# sed -n '$ p' books.txt

4. 指定step跳过多少行
# sed -n '1~2 p' books.txt  打印奇数行
# sed -n '2~2 p' books.txt  打印偶数行

4.3 指定两个地址

用逗号分隔,匹配地址范围中的行,包括这两个首尾地址,并且数字行号地址和正则表达式地址可以混用

1. 打印第1到3行
# sed -n '1,3 p' books.txt

2. 打印第一个包含Tolkien字符串的行到第一个包含Coelho字符串的行
# sed -n '/Tolkien/,/Coelho/ p' books.txt

3. 打印第1行到第一个包含Coelho字符串的行
# sed -n '1,/Tolkien/ p' books.txt

4. 打印第一个包含Tolkien字符串的行以及它后续的2行
# sed -n '/Tolkien/,+2 p' books.txt

5. 打印第一个包含Tolkien字符串的行以及它后续的行,直到行号等于3的倍数(即3×2=6,到第6行为止)
# sed -n '/Tolkien/,~3 p' books.txt

4.4 地址后面带感叹号!

表示不匹配前面的地址

1. 不打印第1行
# sed -n '1! p' books.txt

2. 不打印第一个包含Tolkien字符串的行到第一个包含Coelho字符串的行
# sed -n '/Tolkien/,/Coelho/! p' books.txt

5. 常用命令

5.1 用于0个或1个地址的命令

(1) =

打印匹配行的行号

1. 打印所有包含Tolkien的行号
# sed -n '/Tolkien/ =' books.txt

2. 查询文件总共多少行,即打印最后一行的行号
# sed -n '$ =' books.txt

(2) a

在匹配行的后面追加文本

# sed -i '/Tolkien/ a Hello World' books.txt

(3) i

在匹配行的前面添加文本

# sed -i '/Tolkien/ i Hi Linux' books.txt

(4) q

匹配到后直接退出sed命令,不再读取后续的行。退出命令可以自定义exit status。Q也是退出命令,是GNU sed所特有的扩展

1. q命令会输出完匹配行的内容后才结束sed命令
# sed '/Tolkien/ q' books.txt 
1) A Storm of Swords, George R. R. Martin, 1216 
2) The Two Towers, J. R. R. Tolkien, 352

2. Q命令匹配到行后直接结束sed命令
# sed '/Tolkien/ Q' books.txt 
1) A Storm of Swords, George R. R. Martin, 1216

3. 自定义命令返回值
# sed '/Tolkien/ Q99' books.txt 
1) A Storm of Swords, George R. R. Martin, 1216 
# echo $?
99

(5) r

从其它文件中读取内容,再追加到匹配行的后面

# echo -e "I Love Linux\nHello World" > other.txt
# sed '2 r other.txt' books.txt 
1) A Storm of Swords, George R. R. Martin, 1216 
2) The Two Towers, J. R. R. Tolkien, 352 
I Love Linux
Hello World
3) The Alchemist, Paulo Coelho, 197 
4) The Fellowship of the Ring, J. R. R. Tolkien, 432 
5) The Pilgrimage, Paulo Coelho, 288 
6) A Game of Thrones, George R. R. Martin, 864

5.2 用于任意地址的命令

(1) c

用后续的文本替换匹配的行(或地址范围,或整个文件)

# sed '3,$ c Replace the selected lines with text' books.txt
1) A Storm of Swords, George R. R. Martin, 1216 
2) The Two Towers, J. R. R. Tolkien, 352 
Replace  the  selected  lines with text

(2) d

删除pattern space空间的内容,并且sed进入下一次循环(直接读取下一行),所以d命令后面的其它命令将不会被执行

1. 多个命令如果要作用于同一组地址上,可以用大括号包括所有的命令。d命令后面的=命令不会执行,即不会打印匹配行的行号
# sed -n '1,3 {d ; =}' books.txt

2. 非d命令后面的命令会被执行
# sed -n '1,3 {p ; =}' books.txt
1) A Storm of Swords, George R. R. Martin, 1216 
1
2) The Two Towers, J. R. R. Tolkien, 352 
2
3) The Alchemist, Paulo Coelho, 197 
3

(3) e

匹配行之前,执行外部命令

1. 在第1行前执行whoami命令
# sed '1 e whoami' books.txt
root
1) A Storm of Swords, George R. R. Martin, 1216 
2) The Two Towers, J. R. R. Tolkien, 352 
3) The Alchemist, Paulo Coelho, 197 
4) The Fellowship of the Ring, J. R. R. Tolkien, 432 
5) The Pilgrimage, Paulo Coelho, 288 
6) A Game of Thrones, George R. R. Martin, 864

2. 如果e命令后没有参数,将会把INPUTFILE的每一行当作外部命令执行
# echo -e "whoami\ndate\nlsblk" > commands.txt
# sed 'e' commands.txt

(4) l

显示匹配行内容中隐藏的字符,如tab制表符\t、行尾结束控制符$

# sed -n '1,3 l' books.txt

(5) p

打印当前pattern space的内容

# sed -n '1 p' books.txt

(6) y

语法[addresses]y/SET1/SET2/,匹配行后,将SET1字符串中的各个字符,按对应的位置转换为SET2中的各字符(SET1和SET2的大小必须一致),类似于tr命令

# echo "hello world" | sed 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'
HELLO WORLD
# echo "hello world" | tr a-z A-Z
HELLO WORLD

(7) w

匹配行后,将pattern space的内容以追加的方式写入其它文件中

# sed -n '1,3 w other.txt' books.txt
# cat other.txt 
1) A Storm of Swords, George R. R. Martin, 1216 
2) The Two Towers, J. R. R. Tolkien, 352 
3) The Alchemist, Paulo Coelho, 197

(8) n

先输出pattern space的内容(当前行内容),然后清空它,再提前把当前行的下一行内容读取到pattern space中,n命令之后的命令作用于新pattern space上,所以n命令和d命令都会改变sed的控制流程

# sed '{n; d}' books.txt
1) A Storm of Swords, George R. R. Martin, 1216 
3) The Alchemist, Paulo Coelho, 197 
5) The Pilgrimage, Paulo Coelho, 288

(9) s

文本替换,语法[address]s/pattern/replacement/flags,其中address指地址,s是命令名,pattern是替换命令s的匹配表达式,replacement是对应的替换内容,flags是替换的标志位(标志位可多个组合使用)

1. 没有标志位,默认仅替换文本行第一次被pattern匹配
# echo "A B C D" | sed 's/ /;/'
A;B C D 

2. n取值范围为1-512的数字,表示仅替换第 n 个被pattern匹配的内容 
# echo "A B C D" | sed 's/ /;/3'
A B C;D

3. g全局替换,替换所有被pattern匹配的内容
# echo "A B C D" | sed 's/ /;/g'
A;B;C;D

4. p只有当文本行被pattern匹配时,才打印 pattern space 的内容
# echo "A B C D" | sed 's/ /;/3p'
A B C;D
A B C;D
# echo "A B C D" | sed 's/ /;/5p'
A B C D

5. w filename,只有当文本行被pattern匹配时,才将 pattern space 的内容输出到其它文件中
# echo "A B C D" | sed -n 's/ /;/3w other.txt'
# cat other.txt 
A B C;D

6. 更改默认的分隔符/
# sed -i.bak 's#SELINUX=enforcing#SELINUX=disabled#' /etc/sysconfig/selinux

7. 特殊字符&用于存储匹配模式的内容
# sed 's/[[:digit:]]/Book number &/' books.txt

8. 匹配子字符串,\1-\9 表示被pattern匹配的第1个分组至第9个分组,如果不使用-r选项,要转义字符写成\\1
# sed -i.bak -r 's#(SELINUX=)enforcing#\1disabled#' /etc/sysconfig/selinux

9. 当替换命令的pattern与地址部分是一样的时候,比如/regexp/s/regexp/replacement/可以省略替换命令中的pattern部分,简化为/regexp/s//replacement/。下面的例子将Storm修改为Snow,并在被修改的行前面增加+号
# sed '/Storm/{s//Snow/ ; s/^/+ /}' books.txt
分类: Linux
标签: GNU sed
未经允许不得转载: LIFE & SHARE - 王颜公子 » sed - 文本分析与转换工具 (1) 入门

分享

作者

作者头像

Madman

如果博文内容有误或其它任何问题,欢迎留言评论,我会尽快回复; 或者通过QQ、微信等联系我

0 条评论

暂时还没有评论.

发表评论前请先登录