-
Linux 有三个处理文本的 “三剑客”,各有专长:
-
grep:擅长 “过滤” 文本(挑出符合条件的内容)
-
sed:擅长 “修改” 文本(替换、删除、添加内容等)
-
awk:擅长 “格式化” 输出(按规则拆分和重组内容)
sed 和 awk 是文本处理的两大王牌,简单说:
-
sed 重点:替换文本内容
-
awk 重点:分割文本并重新组织
-
基础用法:打印文本
示例1:模拟cat命令打印文件内容
# 创建测试文件 [lyk@controller lab 10:17:53]$ touch data.txt [lyk@controller lab 10:18:12]$ vim data.txt [lyk@controller lab 10:18:33]$ cat data.txt I am studing sed I am www.twle.cn I am a no-work-men I am so handsome # 用sed打印文件内容(''表示不做任何修改,直接输出) [lyk@controller lab 10:18:39]$ sed '' data.txt I am studing sed I am www.twle.cn I am a no-work-men I am so handsome
示例2:从标准输入中读取数据
sed 也能直接处理键盘输入的内容:
[lyk@controller lab 10:18:56]$ sed '' # 输入hello world,并回车 hello world # 输出hello world hello world # 按ctrl+d推出
关键选项:-e、-f、-n
-e 选项:执行多个命令
从命令行读取sed命令,我们需要将 sed 命令使用单引号 ( ''
) 引起来。
[lyk@controller lab 10:19:46]$ sed -e '' data.txt I am studing sed I am www.twle.cn I am a no-work-men I am so handsome [lyk@controller lab 10:20:49]$ sed '' data.txt I am studing sed I am www.twle.cn I am a no-work-men I am so handsome # -e 选项可以多次使用,1d是作用是删除第一行 [lyk@controller lab 10:20:59]$ sed -e '1d' -e '2d' -e '5d' data.txt I am a no-work-men I am so handsome # 不存在第五行,就没删除的效果 # 使用分号(;)分开多个命令 [lyk@controller lab 10:21:26]$ sed -e '1d;2d;5d' data.txt I am a no-work-men I am so handsome
-f 选项:从文件读取命令
如果命令太多,一行写不下,可以把命令存到一个文件里,用 -f
让 sed 读取执行
当把 sed 存储在文件中时,需要注意 每一个 sed 命令独自成一行。
文件的作用仅仅用于存储命令而已,因此存储 sed 命令的文件并没有任何特殊,可以是一个 .txt
文本文件。
# 把要执行的命令写到 scripts 文件(每行一个命令) [lyk@controller lab 10:22:47]$ echo -e "1d\n2d\n5d" > scripts [lyk@controller lab 10:23:25]$ cat scripts 1d 2d 5d # 用-f执行 scripts 里的命令 [lyk@controller lab 10:23:31]$ sed -f scripts data.txt I am a no-work-men I am so handsome
备注:适合命令复杂的场景,方便保存和复用
-n 选项:只输出指定内容
默认情况下,sed 会自动输出所有处理后的内容。-n
可以关闭这个 “自动输出”,只打印我们明确指定要输出的内容(配合 p
命令)
# 加-n但不指定打印,结果无输出 [lyk@controller lab 10:23:45]$ sed -n '' data.txt # 加-n和p命令:只打印第1行(p=打印) [lyk@controller lab 10:24:25]$ sed -n '1p' data.txt I am studing sed
备注:-n
+ p
是 “精准输出” 的黄金搭档,想打印哪行就指定哪行
sed 行寻址:按行号定位内容
作用
通过行寻址匹配要处理的输入流。
-
语法(以打印为例):
[行号1[,行号2]]p
-
只写一个行号:打印指定行
-
行号 1, 行号 2:打印从行号 1 到行号 2 的所有行
-
$ 表示 “最后一行”
-
示例1
# 先准备一个 test 文件,内容是5行 [lyk@controller lab 10:24:29]$ echo 'This is 1 > This is 2 > This is 3 > This is 4 > This is 5 ' > test [lyk@controller lab 10:25:39]$ cat test | sed '' This is 1 This is 2 This is 3 This is 4 This is 5 #等同于 [lyk@controller lab 10:26:05]$ cat test | sed -n 'p' This is 1 This is 2 This is 3 This is 4 This is 5 # -n 关闭sed打印模式缓冲区中所有内容。 # p命令,明确打印输出模式缓冲区中所有内容。
示例2: 打印特定行
# 打印第1行 [lyk@controller lab 10:26:10]$ cat test | sed -n '1p' This is 1 # 打印最后一行 [lyk@controller lab 10:26:53]$ cat test | sed -n '$p' This is 5
示例3: 打印第1行到3行
[lyk@controller lab 10:27:45]$ cat test | sed -n '1,3p' This is 1 This is 2 This is 3
示例4: 打印第3行到最后一行
[lyk@controller lab 10:27:49]$ cat test | sed -n '3,$p' This is 3 This is 4 This is 5
示例5: 连续输出,打印第2行以及后续两行
[lyk@controller lab 10:28:08]$ cat test | sed -n '2,+2p' This is 2 This is 3 This is 4
示例6: 隔行输出,打印第1行以及后续隔2行输出
[lyk@controller lab 10:28:25]$ cat test | sed -n '1~2p' This is 1 This is 3 This is 5
sed 模式寻址:按内容定位行
除了行号,sed 还能按 “内容匹配” 定位行(支持字符串或正则表达式),就像按关键词找内容
语法:
/模式/p
-
模式可以是普通字符串(如
zhang
) -
也可以是正则(如
^root
表示以 root 开头)
示例文件(用 test 文件,内容是系统用户信息):
[lyk@shell ~]$ cat << 'EOF' > ~/test root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/bin/false daemon:x:2:2:daemon:/sbin:/bin/false mail:x:8:12:mail:/var/spool/mail:/bin/false ftp:x:14:11:ftp:/home/ftp:/bin/false &nobody:$:99:99:nobody:/:/bin/false zhangy:x:1000:100:,,,:/home/zhangy:/bin/bash http:x:33:33::/srv/http:/bin/false dbus:x:81:81:System message bus:/:/bin/false hal:x:82:82:HAL daemon:/:/bin/false mysql:x:89:89::/var/lib/mysql:/bin/false aaa:x:1001:1001::/home/aaa:/bin/bash ba:x:1002:1002::/home/zhangy:/bin/bash test:x:1003:1003::/home/test:/bin/bash @zhangying:*:1004:1004::/home/test:/bin/bash policykit:x:102:1005:Po EOF
示例1: 打印含有字符串zhang的行
[lyk@controller lab 10:29:54]$ cat << 'EOF' > ~/test > root:x:0:0:root:/root:/bin/bash > bin:x:1:1:bin:/bin:/bin/false > daemon:x:2:2:daemon:/sbin:/bin/false > mail:x:8:12:mail:/var/spool/mail:/bin/false > ftp:x:14:11:ftp:/home/ftp:/bin/false > &nobody:$:99:99:nobody:/:/bin/false > zhangy:x:1000:100:,,,:/home/zhangy:/bin/bash > http:x:33:33::/srv/http:/bin/false > dbus:x:81:81:System message bus:/:/bin/false > hal:x:82:82:HAL daemon:/:/bin/false > mysql:x:89:89::/var/lib/mysql:/bin/false > aaa:x:1001:1001::/home/aaa:/bin/bash > ba:x:1002:1002::/home/zhangy:/bin/bash > test:x:1003:1003::/home/test:/bin/bash > @zhangying:*:1004:1004::/home/test:/bin/bash > policykit:x:102:1005:Po > EOF #返回根目录找test [lyk@controller lab 10:35:39]$ cd
[lyk@controller ~ 10:36:01]$ cat test | sed -n '/zhang/p' zhangy:x:1000:100:,,,:/home/zhangy:/bin/bash ba:x:1002:1002::/home/zhangy:/bin/bash @zhangying:*:1004:1004::/home/test:/bin/bash
示例2: 打印root开头的行到zhang开头的行
[lyk@controller ~ 10:36:20]$ cat test | sed -n '/^root/,/^mail/p' root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/bin/false daemon:x:2:2:daemon:/sbin:/bin/false mail:x:8:12:mail:/var/spool/mail:/bin/false
示例3: 打印root开头的行到第三行
[lyk@controller ~ 10:37:34]$ cat test | sed -n '/^root/,3p' root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/bin/false daemon:x:2:2:daemon:/sbin:/bin/false
示例4: 打印root开头的行到最后一行
[lyk@controller ~ 10:37:48]$ cat test | sed -n '/^root/,$p' root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/bin/false daemon:x:2:2:daemon:/sbin:/bin/false mail:x:8:12:mail:/var/spool/mail:/bin/false ftp:x:14:11:ftp:/home/ftp:/bin/false &nobody:$:99:99:nobody:/:/bin/false zhangy:x:1000:100:,,,:/home/zhangy:/bin/bash http:x:33:33::/srv/http:/bin/false dbus:x:81:81:System message bus:/:/bin/false hal:x:82:82:HAL daemon:/:/bin/false mysql:x:89:89::/var/lib/mysql:/bin/false aaa:x:1001:1001::/home/aaa:/bin/bash ba:x:1002:1002::/home/zhangy:/bin/bash test:x:1003:1003::/home/test:/bin/bash @zhangying:*:1004:1004::/home/test:/bin/bash policykit:x:102:1005:Po
sed 子命令
-
打印:p 和 P
-
p
:打印模式空间里的所有内容(可以是一行或多行) -
P
:只打印模式空间里的第一行
-
# 示例:用N把两行合并到模式空间,p打印全部,P只打印第一行 [lyk@controller web 10:39:22]$ echo 'This is 1 > This is 2 > This is 3' | sed -n '1{N;p}' # N=把下一行加到当前行后面 #输出 This is 1 This is 2 # p打印合并后的两行 [lyk@controller web 10:39:32]$ echo 'This is 1 > This is 2 > This is 3' | sed -n '1{N;P}' #输出 This is 1
读取下一行
-
n
:读取下一行,覆盖当前行(当前行会自动输出,除非用 -n) -
N
:读取下一行,追加到当前行后面(两行合并成一行处理,中间有换行符)
示例1:打印偶数行内容
[lyk@controller web 10:39:52]$ echo 'This is 1 > This is 2 > This is 3 > This is 4 > This is 5' | sed -n 'n;p' # n=跳过当前行,p=打印下一行 #输出 This is 2 This is 4
原理:先读第 1 行,n 跳到第 2 行,p 打印;再读第 3 行,n 跳到第 4 行,p 打印...
用 N 合并行
示例1: 成对合并行
[lyk@controller web 10:41:25]$ cd # 把相邻两行用 == 连接(N合并两行,s替换换行符) [lyk@controller ~ 10:41:29]$ cat test | sed 'N;s/\n/==/' root:x:0:0:root:/root:/bin/bash==bin:x:1:1:bin:/bin:/bin/false daemon:x:2:2:daemon:/sbin:/bin/false==mail:x:8:12:mail:/var/spool/ma ......
说明:
-
N
,追加下一行到当前行后面,组成一个新行来处理。 -
s/\n/==/
,将新行中\n
换行符替换成==
,末尾的换行符不替换
示例2: 打印前2行
[lyk@controller ~ 10:41:31]$ echo 'This is 1 > This is 2 > This is 3 > This is 4 > This is 5' | sed -n '1{N;p}' #输出 This is 1 This is 2
替换:s / 旧内容 / 新内容 /
常用选项:
-
不加选项:只替换每行中第一个匹配的内容
-
g
:全局替换(替换一行中所有匹配的内容) -
配合
-n
和p
:只打印发生替换的行
示例1:把test文件中的root替换成tankzhang,只不过只替换一次即终止在这一行的操作,并转到下一行
[lyk@controller ~ 10:42:14]$ sed 's/root/tankzhang/' test|grep tankzhang tankzhang:x:0:0:root:/root:/bin/bash
示例2:把test文件中的root全部替换成tankzhang。字母g是global的缩写
[lyk@controller ~ 10:42:48]$ sed 's/root/tankzhang/g' test |grep tankzhang tankzhang:x:0:0:tankzhang:/tankzhang:/bin/bash
示例3:加了-n
和p
后表示只打印那些发生替换的行(部分替换),下面的例子,不需要使用grep命令
[lyk@controller ~ 10:43:07]$ sed -n 's/root/tankzhang/p' test tankzhang:x:0:0:root:/root:/bin/bash
示例4:加了-n
和pg
后表示只打印那些发生替换的行(全部替换)
[lyk@controller ~ 10:43:23]$ sed -n 's/root/tankzhang/gp' test tankzhang:x:0:0:tankzhang:/tankzhang:/bin/bash
示例5:在第二行到第八行之间,替换以zhang开头的行,用ying来替换,并显示替换的行
[lyk@controller ~ 10:43:37]$ sed -ne '2,8s/^zhang/ying/gp' test yingy:x:1000:100:,,,:/home/zhangy:/bin/bash
示例6: 从以zhang开头的行开始,到匹配Po的行结束,在他们之间进行替换
[lyk@controller ~ 10:43:51]$ sed -ne '/^zhang/,/Po/ s/zhang/ying/gp' test yingy:x:1000:100:,,,:/home/yingy:/bin/bash ba:x:1002:1002::/home/yingy:/bin/bash @yingying:*:1004:1004::/home/test:/bin/bash
替换中的分隔符可以自定义,默认是/
示例7: 自定义替换分隔符为 #
[lyk@controller ~ 10:44:07]$ sed -n 's#root#hello#gp' test hello:x:0:0:hello:/hello:/bin/bash
分隔符;和-e选项
需要执行多个sed处理命令时,用分号分开,或者使用 -e
选项。
示例:
-
在第2行到第8行之间,替换以zhang开头的行,用ying来替换
-
在第5行到第10行之间,用goodbay来替换dbus,并显示替换的行
[lyk@controller ~ 10:44:48]$ cat test | sed -n '2,8s/^zhang/ying/gp;5,10s#dbus#goodbay#gp' yingy:x:1000:100:,,,:/home/zhangy:/bin/bash goodbay:x:81:81:System message bus:/:/bin/false [lyk@controller ~ 10:47:50]$ cat test | sed -ne '2,8s/zhang/ying/gp' -ne '5,10s#dbus#goodbay#gp' yingy:x:1000:100:,,,:/home/yingy:/bin/bash goodbay:x:81:81:System message bus:/:/bin/false
插入
a 在匹配行下面插入新行
将要插入的东西,插入到匹配行的下面
[lyk@controller ~ 10:48:25]$ sed '/root/a====aaaa====' test root:x:0:0:root:/root:/bin/bash ====aaaa==== bin:x:1:1:bin:/bin:/bin/false ......
i 在匹配行上面插入新行
将要插入的东西,插入到匹配行的上面
[lyk@controller ~ 10:48:31]$ sed '/root/i====iiii====' test ====iiii==== root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/bin/false daemon:x:2:2:daemon:/sbin:/bin/false ......
删除
-
d
:删除匹配到的所有行 -
D
:只删除匹配到的行中的第一行(适用于合并后的多行)
d 删除
示例1: 删除1,14行
[lyk@controller ~ 10:49:03]$ sed -e '1,14d' test @zhangying:*:1004:1004::/home/test:/bin/bash policykit:x:102:1005:Po
示例2: 删除4以后的行,包括第4行,把$当成最大行数就行了
[lyk@controller ~ 10:49:45]$ sed -e '4,$d' test root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/bin/false daemon:x:2:2:daemon:/sbin:/bin/false
示例3: 删除包括false的行,或者包括bash的行,别忘了加\
[lyk@controller ~ 10:50:09]$ sed -e '/\(false\|bash\)/d' test policykit:x:102:1005:Po
示例4: 删除从匹配root的行,到匹配以test开头的行,中间的行
[lyk@controller ~ 10:50:24]$ sed -e '/root/,/^test/d' test @zhangying:*:1004:1004::/home/test:/bin/bash policykit:x:102:1005:Po
D 删除 示例
删除当前模式空间开端至\n的内容,放弃之后的命令,对剩余模式空间继续执行sed。
示例1:读取最后一行内容
[lyk@controller web 10:51:09]$ echo 'This is 1 > This is 2 > This is 3 > This is 4 > This is 5' | sed 'N;D' #输出 This is 5
说明:
-
读取
This is 1
,执行N,得出This is 1
\nThis is 2
\n,执行D。 -
读取
This is 3
,执行N,得出This is 3
\nThis is 4
\n,执行D。 -
读取
This is 5
,执行N,后续无内容,读取失败,放弃后续命令,正常打印This is 5
示例2:删除偶数行
[lyk@controller web 10:51:12]$ echo 'This is 1 > This is 2 > This is 3 > This is 4 > This is 5' | sed 'n;D' #输出 This is 1 This is 3 This is 5
说明:
-
读取
This is 1
,执行n,This is 2
\n覆盖This is 1
\n,执行D删除This is 2
\n,This is 1
\n没有删除,正常打印This is 1
。 -
读取
This is 3
,执行n,This is 4
\n覆盖This is 3
\n,执行D删除This is 4
\n,正常打印This is 3
。 -
读取
This is 5
,执行n,后续无内容,读取失败,放弃后续命令,正常打印This is 5
其他实用命令
打印行号 =
示例1:
# 先打印行号,再打印内容(= 输出行号) [lyk@controller ~ 10:53:08]$ sed '=' test 1 root:x:0:0:root:/root:/bin/bash 2 bin:x:1:1:bin:/bin:/bin/false 3 daemon:x:2:2:daemon:/sbin:/bin/false ...... [lyk@controller ~ 10:53:10]$ sed '=' test| sed 'N;s/\n/:/' 1:root:x:0:0:root:/root:/bin/bash 2:bin:x:1:1:bin:/bin:/bin/false 3:daemon:x:2:2:daemon:/sbin:/bin/false 4:mail:x:8:12:mail:/var/spool/mail:/bin/false 5:ftp:x:14:11:ftp:/home/ftp:/bin/false ......
大小写转换:y / 原字符 / 目标字符 /
[lyk@controller web 10:54:27]$ echo 'This is 1 > This is 2 > This is 3 > This is 4 > This is 5' | sed 'y/si/SI/' #输出 ThIS IS 1 ThIS IS 2 ThIS IS 3 ThIS IS 4 ThIS IS 5
读取
读写文件:r 和 w
-
r 文件名
:把 “文件名” 中的内容读到匹配行的下面 -
w 文件名
:把匹配到的内容写入 “文件名” 中
示例: 读取test2的内容,并将其写入到匹配行的下面
[lyk@controller ~ 10:55:12]$ vim test2 ============= ------------- +++++++++++++ [lyk@controller ~ 10:56:02]$ sed -e '/^root/r test2' test root:x:0:0:root:/root:/bin/bash ============= ------------- +++++++++++++ bin:x:1:1:bin:/bin:/bin/false daemon:x:2:2:daemon:/sbin:/bin/false ......
写入
w 写入
将模式空间中记录写入到文件中。
示例1: 将root开头的行,写入test3中
[lyk@controller ~ 10:56:20]$ sed -n '/^root/w test3' test [lyk@controller ~ 10:56:56]$ cat test3 root:x:0:0:root:/root:/bin/bash
W 写入
将模式空间中第一条记录写入到文件中。
示例: 写入记录
# 小写w写入 [lyk@controller ~ 10:56:58]$ vim scripts 1{ N w write.log } [lyk@controller ~ 10:57:30]$ sed -n -f scripts test # 小写w写入包含模式中所有行 [lyk@controller ~ 10:57:36]$ cat write.log root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/bin/false # 大写W写入只包含模式中第一行 [lyk@controller ~ 10:58:00]$ vim scripts 1{ N W write.log } [lyk@controller ~ 10:58:11]$ sed -n -f scripts test [lyk@controller ~ 10:58:20]$ cat write.log root:x:0:0:root:/root:/bin/bash
更改
整行替换
示例:root开头行替换出hello
[lyk@controller ~ 10:57:45]$ sed '/^root/chello' test hello bin:x:1:1:bin:/bin:/bin/false daemon:x:2:2:daemon:/sbin:/bin/false mail:x:8:12:mail:/var/spool/mail:/bin/false ...... # 等同以下命令 [lyk@controller ~ 10:59:49]$ sed 's/^root.*/hello/' test hello bin:x:1:1:bin:/bin:/bin/false daemon:x:2:2:daemon:/sbin:/bin/false mail:x:8:12:mail:/var/spool/mail:/bin/false ......