Sed三剑客入门与进阶

2022-09-28 20:39:15 浏览数 (1)

[TOC]

0x00 快速入门

描述:功能强大的流式文本编辑器(流编辑器-Stream editor)进行文本过滤与格式化替换输出,是在进行文本处理中非常实用的工具,能够完美的配合正则表达式使用;

Sed主要用来自动编辑一个或多个文件,简化对文件的反复操作,编写转换程序等,处理时把当前处理的行存储在临时缓冲区中,称“模式空间”(pattern space),可以指定仅仅处理哪些行

sed 命令脑图

Sed执行流程:

  • 将要处理的文本文件通过指定文本文件路径或者管道输入;
  • 循环读取文本中的行到模式空间,进行判断是否要被处理的行;
  • 执行sed命令进行进一步的处理缓冲区中的内容;
  • 打印模式空间 / 清空模式空间;
  • 读取下一行直至文本结束,并将结果输出或者重定向存储输出;

sed 软件有两个内置的存储空间:

  • 模式空间(pattern space) : 是处理时把当前处理的行存储在临时缓冲区中接着用sed命令处理操作模式空间,处理完成后把缓冲区的内容送往屏幕,接着处理下一行这样不断重复直到文件末尾;
  • 保持空间(hold space) :是sed的另外一个缓冲区,用来存放临时数据,sed可以交换保持空间与模式空间数据,但不能在保持空间上执行普通的sed命令;

备注:初始情况下,模式空间和保持空间都是没有内容的,每次循环读取数据的过程中,模式空间的内容都会被清空写入新的内容,但保持空间的内容保持不变,不会再循环中被删除;

命令格式

代码语言:javascript复制
# sed 选项  sed-命令 文件|标准输入|管道符
sed [options] 'command' files   #文件:指定待处理的文本文件列表或者通过管道符(支持多个文件)
sed [options] -f scriptfile files
sed '[地址范围|模式范围] s#{被替换的字符串}#{替换后的字符串}#{替换标准}' [输入文件]

#组合多个表达式
sed '表达式' | sed '表达式' #等价于下面这一条sed语句
sed '表达式; 表达式'

Sed命令的选项

代码语言:javascript复制
#选项参数
-e <script>或--expression=<script>:#以选项中的指定的script来处理编辑输入的文本文件,可以执行多条sed命令
-f <script文件>或--file=<script文件>:#以选项中指定的script文件来处理输入的文本文件
-i :#用于sed修改的结果直接修改读取数据的文件,而不有Screen输出.(直接在源文件里加入)
-n或--quiet或--silent:#只列出结果sed特殊处理的那一行,不显示原来那一行
-r :支持扩展表达式sed里面()就不需要使用();

示例解释:

代码语言:javascript复制
sed 'Ms#[源字符串]#[替换的字符]#[Ns|N]' file
#Ms : 对第M行操作 无g标志对匹配的第1列处理;有g则对着一行操作;
#Ng :  从第N处/列后面全部开始替换
#Ms Ng : 对第M行从第N出开始匹配替换
#N :从N处进行匹配替换一次 1<N<512

[root@master tmp]# sed '2s#1#0#2' test.txt
1 1 1 1 1
1 0 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
[root@master tmp]# sed '2s#1#0#2g' test.txt
1 1 1 1 1
1 0 0 0 0
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1

0x01 命令替换标记

sed命令标志

代码语言:javascript复制
a 在当前行下面插入文本。
i 在当前行上面插入文本。
c 把选定的行改为新的文本(类似于替换)。
d 删除选择的行
D 删除模板块的第一行
s 替换指定字符
h 拷贝模板块的内容到内存中的缓冲区。
H 追加模板块的内容到内存中的缓冲区。
g 获得内存缓冲区的内容,并替代当前模板块中的文本。
G 获得内存缓冲区的内容,并追加到当前模板块文本的后面。
l 打印显示特殊字符(行尾以及tab标志)
n 读取下一个输入行,用下一个命令处理新的行而不是用第一个命令。
N 追加下一个输入行到模板块后面并在二者间嵌入一个新行,改变当前行号码。
p 显示打印模板块的行。
P(大写) 打印模板块的第一行。
q 退出Sed。
b lable 分支到脚本中带有标记的地方,如果分支不存在则分支到脚本的末尾。
r file 从file中读行。
t label if分支,从最后一行开始,条件一旦满足或者T,t命令,将导致分支到带有标号的命令处,或者到脚本的末尾。
T label 错误分支,从最后一行开始,一旦发生错误或者T,t命令,将导致分支到带有标号的命令处,或者到脚本的末尾。
w file 写并追加模板块到file末尾。  
W file 写并追加模板块的第一行到file末尾。  
! 表示后面的命令对所有没有被选定的行发生作用。  
= 打印当前行号码。  
# 把注释扩展到下一个换行符以前。

sed替换标记

代码语言:javascript复制
1-512 数字标志确定处理第几个字符处开始处理,
i 忽略大小写进行替换/匹配/查找
l 小写的l,它会把紧跟再其后面的字符当作小写字符来处理
L 大写的L,他会把后面所有的字符都当作小写字符来处理
u 他会紧跟在其后的字符当作大写字符来处理
U 他会把后面的所有的字符当作大写字符来处理
E 需要和U和L标志使用,他会把后面的所有字符都当作大写字符来处理
e 执行命令标志,将模式空间的任何内容当作bash命令执行
g 表示行内全面替换。  
p 表示打印行。  
w 表示把行写入一个文件。  
x 表示互换模板块中的文本和缓冲区中的文本。  
y 表示把一个字符翻译为另外的字符(但是不用于正则表达式)
1 子串匹配标记
& 已匹配字符串标记

sed元字符集-即正则表达式

代码语言:javascript复制
^ 匹配行开始,如:/^sed/匹配所有以sed开头的行。
$ 匹配行结束,如:/sed$/匹配所有以sed结尾的行。
. 匹配一个非换行符的任意字符,如:/s.d/匹配s后接一个任意字符,最后是d。
* 匹配0个或多个字符,如:/*sed/匹配所有模板是一个或多个空格后紧跟sed的行。
[] 匹配一个指定范围内的字符,如/[ss]ed/匹配sed和Sed。  
[^] 匹配一个不在指定范围内的字符,如:/[^A-RT-Z]ed/匹配不包含A-R和T-Z的一个字母开头,紧跟ed的行。
(..) 匹配子串,保存匹配的字符,如s/(love)able/1rs,loveable被替换成lovers。
& 保存搜索字符用来替换其他字符,如s/love/**&**/,love这成**love**。
< 匹配单词的开始,如:/<love/匹配包含以love开头的单词的行。
> 匹配单词的结束,如/love>/匹配包含以love结尾的单词的行。
x{m} 重复字符x,m次,如:/0{5}/匹配包含5个0的行。
x{m,} 重复字符x,至少m次,如:/0{5,}/匹配至少有5个0的行。
x{m,n} 重复字符x,至少m次,不多于n次,如:/0{5,10}/匹配5~10个0的行。

0x02 sed选项案例

定界符 描述:在sed中常用字符 / 作为定界符使用,当然您也可以使用任意的定界符;当定界符出现在样式内部时,需要进行转义

代码语言:javascript复制
#实例1.常用定界符
sed 's:test:TEXT:g'
sed 's|test|TEXT|g'
sed 's#test#TEXT#g' #推荐方法

#实例2.定界符出现在要被替换的字符串中时候需要进行转义
sed 's//bin//usr/local/bin/g'

WeiyiGeek.自定义定界符

修改写入i命令实例: -i选项可以直接修改源文件中替换删除的字符串,也能进行备份将要修改的文件;

代码语言:javascript复制
#采用-i 选项 【.bak文件后缀】,进行正则匹配的时候进行先备份将被替换的文件.bak
$ sed -i.bak -e 's/^(PASS_MAX_DAYS).*/1 90/' /etc/login.defs   #以PASS_MAX_DAYS开头 采用字符串匹配标记 1
sed -i.bak 's#22#OK#g' text.txt     #更改的同时进行备份
sed -i.bak '/SELINUX/s/enforcing/disabled/' /etc/sysconfig/selinux  #值得学习

#直接在文件中进行末尾负复制添加:
sed -i '4p' file.txt

WeiyiGeek.

多点编辑e命令实例 -e选项允许在同一行里执行多条命令,常常与下面的-n选项进行联用;

代码语言:javascript复制
#上面sed表达式的第一条命令删除1至5行,第二条命令用check替换test。
#示例1.命令的执行顺序对结果有影响,如果两个命令都是替换命令,那么第一个替换命令将影响第二个替换命令的结果。
sed -e '1,5d' -e 's/test/check/' file
#和 -e 等价的命令是 --expression:
sed --expression='s/test/check/' --expression='/love/d' file
#或者采用一条sed语句执行多条命令 (以 ; 来分割)
sed 's/test/check/;/love/d' file


#示例2.指定单行或者多行不显示
sed -n -e '2!p'  fstab    #表示第二行不显示
sed -n -e '2!p;6!p'       #表示第2行与第6行不显示
sed -n -e '2,5!p'  fstab  #表示第2到5行不现实

#示例3. 多条语句执行,表示删除空行并且以#开头的行
sed -e '/^$/d;/^#/d' fstab   #^$表示首尾相连,也就是空行

显示操作n命令选项示例 描述:利用-n选项p命令来进行指定行的数据查看并且不显示已改变源文件的那一行;

代码语言:javascript复制
#实例1.打印需要显示的行
cat -n ReleaseNotes.css | sed -n "10,13P"
10  a {
11          color: #94C3FF;
12          text-decoration: none;
13  }



#示例2.选定行的范围:,(逗号)**
#所有在模板test和check所确定的范围内的行都被打印:
sed -n '/test/,/check/p' file

#打印从第5行开始到第一个包含以test开始的行之间的所有行:
sed -n '5,/^test/p' file

#对于模板test和west之间的行,每行的末尾用字符串aaa bbb替换:
sed '/test/,/west/s/$/aaa bbb/' file


#示例3.打印奇数行或偶数行(s)
#方法1:
sed -n 'p;n' test.txt  #奇数行
sed -n 'n;p' test.txt  #偶数行
#方法2:sed命令通用的 'n{sed-command}'
sed -n '1~2p' test.txt  #奇数行 1 3 5 7 9  ~后面的数字是Step
sed -n '2~2p' test.txt  #偶数行 2 4 6 8 10
sed -n '2p' test.txt    #打印第二行
sed -n '2,10p' test.txt  #打印二至十行
sed -n '2, 20p' test.txt  #打印二至十二行
sed -n '/test/, 20p' test.txt #打印test字符串行到,前面行 20行截止
sed -n '2,$ p' test.txt #从第二行答应到末尾 (注意空格)
sed -n '/test/,/text/p' test.txt #打印test字符串行到text字符串的行之间的行 (自己组合上面的来达到各种效果)


#示例2.与正则联用打印
sed -n '/^#/p' fstab      #表示显示以#开头的行
sed -n '/^#/!p' fstab     #表示以#开头的行不显示
sed -n '/0$/!p' fstab     #表示以0结尾的行不显示
sed -n '/0$/p' fstab      #表示显示以0结尾结尾的行
sed -n '/^#/!p' /etc/ssh/sshd_config


#实例3.可直接替换配置文件文本
sed -n "s/^(PermitRootLogin).*/1 no/p" /etc/ssh/sshd_config
#PermitRootLogin no

#读取配置文件,利用正则查找指定的行不进行删除(!d)然后替换(s)=号及前的字符串为空;
ReadConfig() {
  # 使用方法: ReadConfig <配置文件> <读取参数>
  # Example : ReadConfig "/etc/config.cfg" "Parameter"
  cat $1 | sed '/^'$2'=/!d;s/.*=//'
}

WeiyiGeek.奇数与偶数行打印

_总结_:

  • sed 匹配是贪婪模式匹配到越多;

正则扩展命令sed之-r选项 描述:使用了该-r选择后就能使用正则表达式的扩展语法,在写一些正则符号前无需添加(

代码语言:javascript复制
#这里url是存储着url的文件:
sed -nr 's/^http://([0-9.] ):[0-9] .*$/1/p' url # 取出IP地址
sed -nr 's/^http://[0-9.] :([0-9] ).*$/1/p' url # 取出端口
sed -nr 's/^http://[0-9.] :[0-9] (.*$)/1/p' url # 取出后续路径

选项执行脚本中的sed表达式 sed脚本是一个sed的命令清单,启动Sed时以-f选项引导脚本文件名。 Sed对于脚本中输入的命令非常挑剔,在·命令的末尾不能有任何空白或文本,如果在一行中有多个命令,要用分号分隔`以#开头的行为注释行,且不能跨行。

代码语言:javascript复制
sed [options] -f scriptfile file(s)
$ sed -f '脚本文件' file
#建立一个sed脚本文件 sed.script 每一行一条
1,2d
s#1#0#3g

$ sed -f sed.script test.txt  
1 1 0 0 0
1 1 0 0 0
1 1 0 0 0

0x03 增删改查命令

单行插入和多行插入

代码语言:javascript复制
#头部插入(行上):i命令
#将 this is a test line 追加到以test开头的 行前面:
sed '/^test/ithis is a test line' file
#单行插入:在test.conf文件第5行之前插入this is a test line
sed -i '5ithis is a test line' test.conf
#多行插入:在test.conf文件第5行前插入first insert n seconde insert
sed -i '5i first insertnseconde insert' test.conf

#尾部追加插入(行下):a命令
#将 this is a test line 追加到 以test 开头的 行后面:
sed '/^test/athis is a test line' file
#单行插入:在 test.conf 文件第2行之后插入 this is a test line:
sed -i '2athis is a test line' test.conf
#多行插入:在test.conf文件第5行后插入first insert n seconde insert
sed -i '5a first insertnseconde insert' test.conf

示例1.优化ssh配置文件写入

代码语言:javascript复制
sed -i '13i Port 52113nPermirRootLogin nonPermitEmptyPasswords nonUseDNS nonGSSAPIAuthentication no' /etc/ssh/sshd_config

删除操作 描述:删除匹配的字符串以及行采用d命令,源文件并没有改变如果改变需要加上选项-i

代码语言:javascript复制
#更多格式需要参考:示例3.打印奇数行或偶数行(s)
sed 'd' file       #删除文件的所有行
sed '2d' file       #删除文件的第2行
sed '2,5d' file       #删除文件的第2行到第五行
sed '$d' file       #删除文件最后一行
sed '2,$d' file     #删除文件的第2行到末尾所有行
sed '/^test/d' file # 删除文件中所有开头是test的行注意是一行
sed '/^$/d' file    # 删除空白行

示例2:匹配的行(文本、正则)进行删除

代码语言:javascript复制
#(1) 删除或者不删除指定字符串的行
sed -e '/UUID/d' fstab   #表示删除含有UUID的行
sed -e '/UUID/!d' fstab  #表示不删除含有UUID的行,也就是只显示含有UUID的行

#(2) 以正则表达式匹配删除
sed -e '/^#/d' fstab   #表示删除以#开头的行
sed -e'/^#/d;/^$/d' _config.yml  #采用多行命令,后面的命令以前面命令为基础;
# include:
#   - .nojekyll
# exclude:
#   - .DS_Store
# search:
#   path: search.xml
#   filed: post

替换操作

描述:替换文本的字符串,采用s命令以及g来进行全局替换,也可以采用c命令指定行替换;

代码语言:javascript复制
######### 行替换(多行/单行) ############
#将text.txt文本的第二行替换成为"second insert txt" (支持多行和单行替换)
$ sed '2c second insert n there txt' text.txt

############## 字符替换操作 ###############
#-n选项和p命令一起使用表示只打印那些发生替换的行(并未写入文件中)
$ sed -n 's/test/TEST/p' file 
$ sed 's/book/books/' file  #将文件中的第一个book字符串换成books
$ sed '3s/60/90/' file.txt # 将text.txt第三行中得60换成90;

#直接编辑文件-i选项,会匹配file文件中每一行的book替换为books(直接写入到源文件中)
$ sed -i 's/book/books/g' file #使用后缀 /g 标记会替换每一行中的所有匹配:
$ sed -i 's/book/books/4g' file #每行从第四个book字符串开始替换

#开始从第二个匹配字符串进行替换
$ sed 's/test/TEST/2g' www  
testTESTTEST
testTESTTEST

################ 变量替换 ################
#sed表达式可以使用单引号来引用,但是如果`表达式内部包含变量字符串,就需要使用双引号`。
test=hello
echo hello WORLD | sed "s/$test/HELLO"
HELLO WORLD


################ 分组替换 ################
描述:就是正则表达式中的元组即(...)包含匹配的字符串,即匹配给定样式的其中一部分,
(..) 用于匹配子串,对于匹配到的第一个子串就标记为 1,依此类推匹配到的第二个结果就是 2,例如:
#实例1.样式匹配到的子串是 7,命令中 digit 7,被替换成了 7
echo this is digit 7 in a number | sed 's/digit ([0-9])/1/'
#this is 7 in a number

实际示例:

代码语言:javascript复制
#行替换/字符替换
sed "/SELINUX/s/disabled/enforcing/#" etc/selinux/config
sed "/^SELINUX/s/disabled/enforcing/#" /etc/selinux/config

#变量替换
x=a;y=b #将字符a替换成为b(四种形式),
sed "s#$x#$y#g" test.txt
sed s#$x#$y#g test.txt
sed 's#'$x'#'$y'#g' test.txt #注意单引号不能解析变量
eval sed 's#'$x'#'$y'#g' test.txt 


#分组替换
#love被标记为1,所有loveable会被替换成lovers,并打印出来.组合多个表达式 (值得学习借鉴)
echo "loveable" | sed -n 's/(love)able/1rs/p;s/(love)rs/1 You/p'  #lovers  #love You
$echo aaa BBB | sed 's/([a-z] ) ([A-Z] )/2 1/'  #BBB aaa
#传递每行到shell中进行执行
systemctl list-unit-files | grep "disabled" | grep -E "socket"|awk '{print $1}'|sed -r 's#(.*)#systemctl 1 enable#g'|bash
systemctl docker.socket enable
systemctl rsyncd.socket enable
systemctl sshd.socket enable
systemctl tftp.socket enable

已匹配字符串标记 & 描述: 正则表达式 w 匹配每一个单词,使用 [&] 替换它,& 对应于之前所匹配到的单词

代码语言:javascript复制
#实例1.用来字符串拼接使用
echo this is a test line | sed 's/w /[&]/g'
#[this] [is] [a] [test] [line]

#所有以192.168.0.1开头的行都会被替换成它自已加localhost:
sed 's/^192.168.0.1/& localhost/' file # & 代表匹配字符串本身
#192.168.0.1 localhost 

#批量更改文件名称
$ ls *.jpg | sed -r 's#(.*).finish.*#mv & 1.jpg#'  | bash
mv 1_student_10_finish.jpg 1_student_10.jpg
mv 1_student_1_finish.jpg 1_student_1.jpg
mv 1_student_2_finish.jpg 1_student_2.jpg
mv 1_student_3_finish.jpg 1_student_3.jpg
mv 1_student_4_finish.jpg 1_student_4.jpg
mv 1_student_5_finish.jpg 1_student_5.jpg
mv 1_student_6_finish.jpg 1_student_6.jpg
mv 1_student_7_finish.jpg 1_student_7.jpg
mv 1_student_8_finish.jpg 1_student_8.jpg
mv 1_student_9_finish.jpg 1_student_9.jpg
$ ls *.jpg
1_student_1.jpg   1_student_2.jpg  1_student_4.jpg  1_student_6.jpg  1_student_8.jpg
1_student_10.jpg  1_student_3.jpg  1_student_5.jpg  1_student_7.jpg  1_student_9.jpg

WeiyiGeek.已匹配字符串标记

查操作 描述:采用p命令进行打印常常和-n选项联用,在前面介绍-n的时候有介绍

代码语言:javascript复制
ifconfig | sed -nr 's#([0-9] .){3}[0-9]{1,3}#[&]#gp' #打印出匹配的行
inet [192.168.1.99]  netmask [255.255.255.0]  broadcast [192.168.1.255]
inet [10.10.107.222]  netmask [255.255.255.0]  broadcast [10.10.107.255]
inet [127.0.0.1]  netmask [255.0.0.0]

sed 文件修改,另存为,读取

写入文件:w命令,当然也可以采用重定向的方式也是可以的;

代码语言:javascript复制
############## 文件另存为 ####################
#实例1.在example中所有包含test的行都被写入file里:
sed -n '/test/w file' example
sed -n '/test/' example > file


#实例2.将匹配出的IP输出到文件
ifconfig | sed -nr 'sed -nr 's#(([0-9] .){3}[0-9]{1,3})#[1]#g w output.txt'
$ cat output.txt
inet [192.168.1.99] netmask [255.255.255.0]  broadcast [192.168.1.255]
inet [10.10.107.222]  netmask [255.255.255.0]  broadcast [10.10.107.255]

#示例3.通过正则匹配出的行写入到文件中
sed -n '/bash$/p' passwd > file  #重定向写入文件
sed -n '/bash$/wfile' passwd     #在w模式下,可以直接写入文件,这是追加的过程不会覆盖原文

从文件读入:r命令 描述:file里的内容被读进来,显示在与test匹配的行后面,如果匹配多行,则file的内容将显示在所有匹配行的下面

代码语言:javascript复制
#示例1.读取file文件中的到并且插入到查找到test字符串的后面
sed '/test/r file' filename

#示例2.读取passwd
echo -e "User list:" | sed  'r /etc/passwd'
User list:
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
0x04 其他命令

显示文本或者匹配文本的行数=

代码语言:javascript复制
$ getent passwd | sed -n '=;/root/p'
# 1
# root:x:0:0:root:/root:/bin/bash
# 10
# operator:x:11:0:operator:/root:/sbin/nologin

#改进采用N命令来保持模式空间里面的数据
$ getent passwd | sed -n '=;/root/p' | sed 'N;s#n# #'
1 root:x:0:0:root:/root:/bin/bash
10 operator:x:11:0:operator:/root:/sbin/nologin

忽略大小写进行匹配替换i:

代码语言:javascript复制
$ echo "abc ABC" |sed 's#a#W#ig'
Wbc WBC

执行命令标志e的使用:

代码语言:javascript复制
$ cat txt 
RPM-GPG-KEY-weiyigeek
aaa.jpg
compare_varnish.jpg

#将匹配到的文件相当于执行 ls -lh [filename]
$ sed 's#^#ls -lh & #e' txt   #注意这里命令后需要空格 & ,否则有一定的报错;
-rw-r--r-- 1 root root 1.1K Jun 20 09:13 RPM-GPG-KEY-weiyigeek
-rw-r--r-- 1 root root 106K Jun 27 17:10 aaa.jpg
-rw-r--r-- 1 root root 106K Jun 27 17:11 compare_varnish.jpg

大小写转换l L 与 u U;

代码语言:javascript复制
$ echo "abcdefg" | sed 's#d# WEIYI lGEEK #p' #符号后的一个字符变成小写
abc WEIYI gEEK efg
$ echo "abcdefg" | sed 's#d# WEIYI LGEEK #p' #其后所有字母都变小写
abc WEIYI geek efg
$ echo "abcdefG" | sed 's#d# WEIYI ugeek #p'
abc WEIYI Geek efG
$ echo "abcdefG" | sed 's#d# WEIYI Ugeek #p'
abc WEIYI GEEK efG

#可以将匹配的字符放在U|u|l|L后面即可
echo "abcdefG" | sed  -nr 's#([a-z]{1,10})# WEIYIGEEK U1#p' #全部转成大写
WEIYIGEEK ABCDEFG

大小写变形y命令:注意正则表达式元字符不能使用这个命令

代码语言:javascript复制
#把1~10行内所有abcde转变为大写,
sed '1,10y/abcde/ABCDE/' file

特殊符号 {} 描述:包含在{}中的命令按照顺序执行,并且前面命令会影响后面的命令;

代码语言:javascript复制
#打印匹配字符串的下一行然后并打印
sed -n '/SCC/{n;p}' URFILE  # 5字符的下一行就是6 (值得学习)
grep -A 1 SCC URFILE   # -A 显示的行数 在字符的下面
awk '/SCC/{getline; print}' URFILE   # 5字符的下一行就是6

#只打印指定的行号的选项
sed -n '2,5{=;p}' /etc/passwd
2
bin:x:1:1:bin:/bin:/sbin/nologin
3
daemon:x:2:2:daemon:/sbin:/sbin/nologin
4
adm:x:3:4:adm:/var/adm:/sbin/nologin
5
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin

#进一步完善
sed -n '2,5{=;p}' /etc/passwd | sed  "N;s#n# #g"
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin

WeiyiGeek.补充实例

特殊符号 1

描述:打印显示特殊字符(行尾以及tab标志),小写的L;

代码语言:javascript复制
$echo -e  "abc n cde n hfg t qwe " | sed -n 'l'
abc $
 cde $
 hfg t qwe $

双引号内的命令

代码语言:javascript复制
#退出:q命令
#打印完第10行后,退出sed
sed '10q' file
sed -n '10q;p' /etc/passwd

#保持和互换:h命令和x命令
#互换模式空间和保持缓冲区的内容。也就是把包含test与check的行互换:
sed -e '/test/h' -e '/check/x' file


#保持和获取:h命令和G命令
#在sed处理文件的时候,每一行都被保存在一个叫模式空间的临时缓冲区中,除非行被删除或者输出被取消,否则所有被处理的行都将 打印在屏幕上。接着模式空间被清空,并存入新的一行等待处理。
sed -e '/test/h' -e '$G' file #追加到最后一行简单来说,任何包含test的行都被复制并追加到该文件的末尾。不加$则每一行下一行进行显示复制的数据;

#在这个例子里,匹配test的行被找到后,将存入模式空间,h命令将其复制并存入一个称为保持缓存区的特殊缓冲区内。
#第二条语句的意思是,当到达最后一行后,G命令取出保持缓冲区的行,然后把它放回模式空间中,且追加到现在已经存在于模式空间中的行的末尾。

WeiyiGeek.实例

模式空间 n命令:打印下一行并且清空当前模式空间进入下一次循环 N命令:不清空当前模式空间,然后读入下一行以n分隔两行;

代码语言:javascript复制
#n命令示例
#如果test被匹配,则移动到匹配行的下一行,替换这一行的aa,变为bb并打印该行
sed '/test/{ n; s/aa/bb/; }' file
sed '/test/{ n;p }' file #打印test字符串的下一行 

#N命令示例 (模式空间的n替换为空)
$ sed -n '10{=;p}' /etc/passwd | sed 'N; s#n# #g'
10 news:x:9:9:news:/var/spool/news:/usr/sbin/nologin

#其他用法(值得学习)
$cat txt
name
weiyigeek
age
29
love
computer

$sed 'N;s#n# = #g' txt
name = weiyigeek
age = 29
love = computer

Sed模拟其他命令 模拟:cat text.txt

代码语言:javascript复制
#不改动模式空间内容,既可以输入text文件中的全部内容
sed "" text.txt
sed -n 'p' text.txt
sed 'n' text.txt
sed 'N' text.txt
sed 's# # #g' text.txt

模拟:grep -v反过滤

代码语言:javascript复制
#显示带有字符的行或者不显示带有字符的行
sed -n '/test/ p' text.txt
sed -n '/test/ !p' text.txt

模拟:wc -l命令

代码语言:javascript复制
#统计文件中的行数
sed -n "$=" /etc/passwd

内容中的换行符替换 描述:sed是可以处理多行数据的,N是把下一行加入到当前的hold space模式空间里,使之进行后续处理,最后sed会默认打印hold space模式空间里的内容。

基础实例:

代码语言:javascript复制
# (方式1):a和ta是配套使用,实现跳转功能,t是test测试的意思。 
sed ":a;N;s/n//g;ta" file

# test可以根据替换命令的完成是否成功,决定是否跳转,类比下面的伪代码
# while(state == 1) { #注释:默认state就当是1好了。
#  N;
#  s/n//g;  #注释:成功,返回state为1;否则返回state=0。此state用于跳转判断。
# }else {
#  last; #注释:即退出循环语句。
# }

# (方式2):a和ba的配套使用方式,也可以实现跳转功能,b是branch分支的意思。
sed ":a;N;s/n//g;ba" file
#branch循环到文本结束类比下面的伪代码
# while(1) {
#  N;
#  s/n//g;
# }

#比如:增加$!ba语句,$的意思是最后一行,不跳转到标记a处,即退出命令循环(两种写法)。
sed ":a;N;s/n//g;$!ba" file
sed ":a;$!N;s/n//g;ba" file

#(方式3).替换n也就是把所有的行都归为一行
sed ':a;N;$!ba;s/n/ /g' file.txt 

## 实际可以将上面的命令拆分描述,就比较简单多了;
# 多次匹配替换
sed -i -e 's/12_/abc/g' -e 's/aa_/aa_3rth/g' AAA.txt 
sed -e ':a' -e 'N' -e '$!ba' -e 's/n//g' file.txt

在指定字符前后进行添加插入相应字符

代码语言:javascript复制
# -1.在1111之前添加AAA, 方法 sed -i 's/指定的字符/要插入的字符&/'文件
sed -i 's/1111/AAA&/' /tmp/input.txt

# -2.在1111之后添加BBB,方法 sed -i 's/指定的字符/&要插入的字符/' 文件
sed -i 's/1111/&BBB/' /tmp/input.txt

# -3.在每行的头添加字符比如"HEAD"以及在每行的尾部添加字符比如"END", 命令如下:
sed 's/^/HEAD&/;s/$/&END/' /tmp/input.txt   

# -4.(1) 删除所有空行;(2) 一行中,如果包含"1111",则在"1111"前面插入"AAA",在"11111"后面插入"BBB"
sed '/^$/d;s/1111/AAA&/;s/1111/&BBB/' /tmp/input.txt   

# -5.在包含www.baidu.com的行前面或后面添加多一行内容www.qq.com
# 匹配行前加
sed -i '/www.baidu.com/i www.qq.com' domain.file
# 匹配行后加
sed -i '/www.baidu.com/a www.qq.com' domain.file

# -6.在62行前面或后面添加多一行内容" chmod 644 /data/backup/2015-08-22/* "
# 在指定行前加
sed -i 'N;62 i chmod 644 /data/backup/2015-08-22/*' /home/bin/backup_data.sh
# 在指定行后加
sed -i 'N;62 a chmod 644 /data/backup/2015-08-22/*' /home/bin/backup_data.sh

# -7.在指定行之前插入`*  soft  nofile  65535` 到 /etc/security/limits.conf 中
# sed "/# End/ i *  soft  nofile  65535" /etc/security/limits.conf

# -8.在查询到关键字的行中进行字符串替换
sed -i '/echo "$os" | grub_quote/ { s/menuentry/menuentry --unrestricted/g;}' /etc/grub.d/10_linux

# -9.查找指定行并进行插入指定字符串
linenumber=`expr $(egrep -n "pam_unix.sos$" /etc/pam.d/common-session-noninteractive | cut -f 1 -d ":") - 2`
sudo sed -ri "${linenumber}a session [success=1 default=ignore] pam_succeed_if.so service in cron quiet use_uid" /etc/pam.d/common-session-noninteractive

0 人点赞