sed 是一个比较古老的,功能十分强大的用于文本处理的流编辑器,加上正则表达式的支持,可以进行大量的复杂的文本编辑操作。sed 本身是一个非常复杂的工具,有专门的书籍讲解 sed 的具体用法,但是个人觉得没有必要去学习它的每个细节,那样没有特别大的实际意义。网上也有很多关于 sed 的教程,我也是抱着学习的心态来学习 sed 的常见的用法,并进行系统的总结,内容基本覆盖了 sed 的大部分的知识点。文中的内容比较简练,加以实际示例来帮助去理解 sed 的使用。
一、写在前边
1、sed介绍
sed 全名为 stream editor,流编辑器,用程序的方式来编辑文本,功能相当的强大。是贝尔实验室的 Lee E.McMahon 在 1973 年到 1974 年之间开发完成,目前可以在大多数操作系统中使用,sed 的出现作为 grep 的继任者。与vim等编辑器不同,sed 是一种非交互式编辑器(即用户不必参与编辑过程),它使用预先设定好的编辑指令对输入的文本进行编辑,完成之后再输出编辑结构。sed 基本上就是在玩正则模式匹配,所以,玩sed的人,正则表达式一般都比较强。
2、sed工作原理
sed会一次处理一行内容。处理时,把当前处理的行存储在临时缓冲区中,成为"模式空间",接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出。
3、正则表达式概念
在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具,换句话说,正则表达式就是记录文本规则的代码。许多程序设计语言都支持利用正则表达式进行字符串操作。在很多文本编辑器里,正则表达式通常被用来检索、替换那些符合某个模式的文本。
4、正则表达式的匹配过程
简单描述一下正则表达式的匹配过程,就是拿正则表达式所表示的字符串去和原文字符串内容去匹配,直到匹配到原文内容字符串中的一个完整子串就表示匹配成功。举个例子,有一行文件内容"this is better desk",这里用"esk"去匹配,匹配过程是这样的:首先拿e去匹配文件行内容,从this开始,直到better的e,第一个字符匹配成功,接着s去匹配better字符e后边的t字符,没有匹配成功;然后重新拿esk中的e去和better的第二个t去匹配,没有成功,接着原始内容的下一个字符,直到desk中的e字符,逐个匹配s,k字符,到此为止,esk成功匹配,正则表达式匹配完毕,整个过程就是这样,即使再复杂的正则表达式的匹配过程也是按照此过程来进行的。
二、基本正则表达式
关于正则表达式的内容挺多的,掌握好下文中提及的内容就能满足正常工作中的需要,如果是专门做正则编程的,可以去买本正则表达式的书籍来看好了_。只有多动手多练习,才是学开发编程的最好姿势。
&:用正则表达式匹配的内容进行替换
\n:回调参数
:保存被匹配的字符以备反向引用\n时使用,最多9个标签,标签书序从左到右
Flagsn:可以是1-512,表示第n次出现的情况进行替换
g:全局更改
p:打印模式空间的内容
w file:写入到一个文件file中
实例用法
测试文件:
hello 123 world
例子1:
sed ‘s/hello/HELLO/’ message
说明:将message每行包含的第一个hello的字符串替换为HELLO,这是最基本的用法。
例子2:
sed -r ‘s/[a-z]+ [0-9]+ [a-z]+/A/’ message
结果:A
说明:使用了扩展正则表达式,需要加-r选项。
例子3:
sed -r ‘s/([a-z]+)( [0-9]+ )([a-z]+)/\1\2\3/’ message
结果:hello 123 world
说明:再看下一个例子就明白了。
例子4:
sed -r ‘s/([a-z]+)( [0-9]+ )([a-z]+)/\3\2\1/’ message
结果:world 123 hello
说明:\1表示正则第一个分组结果,\2表示正则匹配第二个分组结果,\3表示正则匹配第三个分组结果。
例子5:
sed -r ‘s/([a-z]+)( [0-9]+ )([a-z]+)/&/’ message
结果:hello 123 world
说明:&表示正则表达式匹配的整个结果集。
例子6:
sed -r ‘s/([a-z]+)( [0-9]+ )([a-z]+)/111&222/’ message
结果:111hello 123 world222
说明:在匹配结果前后分别加了111、222。
例子7:
sed -r ‘s/.*/111&222/’ message
说明:在message文件中每行的首尾分别加上111、222。
例子8:
sed ‘s/i/A/g’ message
说明:把message文件中每行的所有i字符替换为A,默认不加g标记时只替换每行的第一个字符。
例子9:
sed ‘s/i/A/2’ message
说明:把message文件中每行的第2个i字符替换为A。
例子10:
sed -n ‘s/i/A/p’ message
说明:加-p标记会把被替换的行打印出来,再加上-n选项会关闭模式空间打印模式,因此该命令的效果就是只显示被替换修改的行。
例子11:
sed -n ‘s/i/A/w b.txt’ message
说明:把message文件中内容的每行第一个字符i替换为A,然后把修改内容另存为b.txt文件。
例子12:
sed -n ‘s/i/A/i’ message
说明:把message文件中每一行的第一个i或I字符替换为A字符,也即是忽略大小写。
八、sed工作模式
1、模式空间和保持空间
模式空间初始化为空,处理完一行后会自动输出到屏幕并清除模式空间;保持空间初始化为一个空行,也就是默认带一个\n,处理完后不会自动清除。模式空间和保持空间,从程序的角度去看,其实就是sed在工作的时候占用了一些内存空间和地址,sed工作完毕就会把内存释放并归还给操作系统。
2、sed工作流程
大概简单描述一下sed的工作流程,读取文件的一行,存入模式空间,然后进行所有子命令的处理,处理完后默认会将模式空间的内容输出打印到标准输出,也就是在屏幕上显示出来,接着清空模式空间的内存,继续读取下一行的内容到模式空间,继续处理,依次循环处理。
3、模式空间和保持空间的置换
h:把模式空间内容覆盖到保持空间中
H:把模式空间内容追加到保持空间中
g:把保持空间内容覆盖到模式空间中
G:把保持空间内容追加到模式空间中
x:交换模式空间与保持空间的内容
4、实例用法
测试文件:
11111
22222
33333
44444
例子1:
sed ‘{1h;2,3H;4G}’ test.txt
结果:
11111
22222
33333
44444
11111
22222
33333
解释说明:略。懒得写了。
例子2:
sed ‘{1h;2x;3g;KaTeX parse error: Expected 'EOF', got '}' at position 2: G}̲’ test.txt 结果: …!d}’ test.txt
结果:
44444
33333
22222
11111
九、高级子命令
高级子命令比较少,但是比较复杂,平时用的也会相对少些,却也很重要,有的内容处理不用高级子命令是完成不了的。
n:读入下一行到模式空间,例:’4{n;d}’ 删除第5行。
N:追加下一行到模式空间,再把当前行和下一行同时应用后面的命令。
P:输出多行模式空间的第一部分,直到第一个嵌入的换行符位置。在执行完脚本的最后一个命令之后,模式空间的内容自动输出。P命令经常出现在N命令之后和D命令之前。
D:删除模式空间中第一个换行符的内容。它不会导致读入新的输入行,相反,它返回到脚本的顶端,将这些指令应用与模式空间剩余的内容。这3个命令能建立一个输入、输出循环,用来维护两行模式空间,但是一次只输出一行。
例子1:
sed ‘N;KaTeX parse error: Expected 'EOF', got '#' at position 13: !P;D’ a.txt #̲说明:删除文件倒数第二行 例子…!P; ! D ; !D; !D;d’ a.txt
十、分支和测试
分支命令用于无条件转移,测试命令用于有条件转移。
1、分支branch
跳转的位置与标签相关联。
如果有标签则跳转到标签所在的后面行继续执行。
如果没有标签则跳转到脚本的结尾处。
标签:以冒号开始后接标签名,不要在标签名前后使用空格。
2、跳转到标签指定位置
测试文件:
grep seker /etc/passwd
seker❌500:500::/home/seker:/bin/bash
例子1:
grep seker /etc/passwd | sed ‘:top;s/seker/blues/;/seker/b top;s/5/555/’
结果:blues❌55500:500::/home/blues:/bin/bash
选择执行
例子2:
grep ‘seker’ /etc/passwd | sed ‘s/seker/blues/;/seker/b end;s/5/555/;:end;s/5/666/’
结果:blues❌66600:500::/home/seker:/bin/bash
测试命令,如果前一个替换命令执行成功则跳转到脚本末尾(case结构)
例子3:
grep ‘seker’ /etc/passwd | sed ‘s/seker/ABC;t;s/home/DEF/;t;s/bash/XYZ/’
结果:ABC❌500:500::/home/seker:/bin/bash
例子4:
grep ‘zorro’ /etc/passwd | sed ‘s/seker/ABC/;t;s/home/DEF/;t;s/bash/XYZ’
结果:zorro❌500:500::/DEF/zorro:/bin/bash
与标签关联,跳转到标签位置。
例子5:
grep ‘seker’ /etc/passwd | sed ‘s/seker/ABC/;t end;s/home/DEF/;t;end;s/bash/XYZ’
结果:ABC❌500:500::/home/seker:/bin/XYZ
十一、sed实战练习
实例1:删除文件每行的第二个字符。
sed -r 's/(.)(.)KaTeX parse error: Undefined control sequence: \1 at position 2: /\̲1̲/' 实例2:删除文件每行的最…/\1/’
实例3:删除文件每行的倒数第2个单词。
sed -r ‘s/(.)([a-Z]+)([a-Z]+)([a-Z]+)([a-Z]+)([^a-Z]KaTeX parse error: Undefined control sequence: \1 at position 3: )/\̲1̲\2\4\5/’ /etc/p…)/\5\2\3\4\1\6/’ /etc/passwd
实例6:删除一个文件中所有的数字。
sed ‘s/[0-9]//g’ /etc/passwd
实例7:用制表符替换文件中出现的所有空格。
sed -r ‘s/ +/\t/g’ /etc/passwd
实例8:把所有大写字母用括号()括起来。
sed -r ‘s/([A-Z])/(\1)/g’ /etc/passwd
实例9:打印每行3次。
sed ‘p;p’ /etc/passwd
实例10:隔行删除
sed ‘0~2{=;d}’ /etc/passwd
实例11:把文件从第22行到第33行复制到56行后面。
sed ‘22h;23,33H;56G’ /etc/passwd
实例12:把文件从第22行到第33行移动到第56行后面。
sed ‘22{h;d};23,33{H;d};56g’ /etc/passwd
实例13:只显示每行的第一个单词。
sed -r ‘s/([a-Z]+)([^a-Z]+)(.)/\1/’ /etc/passwd
实例14:打印每行的第一个单词和第三个单词。
sed -r ‘s/([a-Z]+)([a-Z]+)([a-Z]+)([a-Z]+)([a-Z]+)([^a-Z]+)(.*)/\1\t\5/’ /etc/passwd
实例15:将格式为mm/yy/dd的日期格式换成 mm;yy;dd
date ‘+%m/%y/%d’ | sed ‘s///;/g’