sed 正则表达式简介

Posted by chunyang on March 19, 2017
TAGS: #shell

sed 是 Linux 文本处理的一个利器。熟练使用它,对简单文本处理非常有帮助。陈皓写过一篇 sed ,比较全面的介绍了 sed。文末还对 sed 的高级部分 pattern space 和 hold space 做了介绍。

相对于酷壳的介绍,本文主要想重点介绍下 sed 的正则表达式部分。因为不掌握好这部分内容,sed 的优势也没 法发挥的彻底。

sed 引用 shell 中的变量

当进行 shell 编程时需要引用 shell 中定义或者导出 (export) 的环境变量,有如下几种方式:

  • eval sed ’s/$a/$b/’ filename
  • sed “s/$a/$b/” filename
  • sed ’s/’$a’/’$b’/’ filename
  • sed s/$a/$b/ filename
pat="HI"
rep="HELLO"

# test.txt
# HI
# HI HELLO

eval sed 's/$pat/$rep/' test.txt

# test.txt
# HELLO
# HELLO HELLO

sed "s/$rep/$pat/" test.txt

# test.txt
# HI
# HI HI

sed 中的正则表达式

下面表格列出了 sed 支持的元字符 (metachar)

元字符 功能 示例
^ 匹配行首 ^#!, 匹配所有#!开头的行
$ 匹配行尾 c$, 匹配所有以c结尾的行
. 匹配单个字符 b.g, 匹配b和g中间是任何字符,例如big,bag
* 匹配0个或者多个字符 *$,匹配末尾是0个或者多个空格
\{m, n\} 匹配某个模式出现至少m次,至多n次 hel\{1,2\}o, 匹配l是一个或者两个
\{m\} 匹配某个模式出现m次 hel\{2\}o, 匹配l是两个
\{m,\} 匹配某个模式出现至少m次 hel\{1,\}o, 匹配l是至少一个
[character-set] 字符集合 [a-z], 匹配a-z中间任意一个字符
[^character-set] 排除字符集合 [^0-9],匹配非数字,但是必须匹配某个字符
\(..\) 用于将多部门内容组成一个整体,方便在替换中引用 \(tag\).*\1, 匹配一个模式,然后重复匹配
\< 匹配单词的左边界 \<hello, 匹配hello,左边是边界
\> 匹配单词的右边界 hello\>, 匹配hello,右边是边界

当然是用元字符作为匹配内容时,需要使用 \ 进行转义。

sed 处理文本的流程

sed 无法处理空文件。sed 的工作机制是读入一行数据,然后处理,然后读入下一行数据进行处理。如果文件 本身是空的,那 sed 不会做任何处理。

利用 sed 在一个空文件中插入行是做不到的。

sed 并不是直接在原文件上进行处理,sed 有两个空间 pattern space 和 hold space。sed 每次读入一行,存 入 pattern space,处理后将 pattern space 输出到终端或者文件。hold space 在一般情况下没有作用,但是 有命令可以在 pattern space 和 hold space 之间进行数据交互。

命令 功能
h 将模式空间里的内容拷贝到暂存缓冲区并替换原来暂存缓冲区的内容
H 将模式空间里的内容追加到暂存缓冲区
g 将里暂存缓冲区的内容拷贝到模式空间并替换原来模式空间的内容
G 将暂存缓冲区里的内容追加到模式空间
n 读入下一行到模式空间
N 读入下一行,增加\n,追加到模式空间
x 交换模式空间与暂存缓冲区的内容

sed 支持的选项

选项 功能
i 匹配行前插入内容
a 匹配行后插入内容
c 替换匹配行
d 删除匹配行
p 打印匹配行
= 打印行号, 注意是另起一行

常见功能实现

给每一行添加行号

这个功能使用 awk 是非常容易实现, 因为它有 NR 这个变量,当然也可以使用自己定义的变量。sed 实现这个 功能还需要使用它的 pattern space 和 hold space。

# test.txt
# HI
# HI HELLO
# HI
# HI HELLO
# HI
# HI HELLO

sed = test.txt | sed 'N;s/\n/\t/'

交换相邻的行

# test.txt
# HI
# HI HELLO
# HI
# HI HELLO
# HI
# HI HELLO

sed -n 'h;n;G;p' test.txt

如果你有什么好玩的想法或者案例,欢迎交流和贡献。

本文完