sed:Stream Editor文本流编辑,sed是一个“非交互式的”面向字符流的编辑器。本文主要以实际的数据来介绍sed的substitude(替换)即s命令的使用。
替换是工作中经常用的应用场景,比如在拼接较多表的SQL命令时,即可通过sed的S命令进行字符的替换以达到拼接字符串的目的。S命令即是删除行 (substitude)是对sed的输出流里的数据进行替换而不是直接作用到文件本身。
注: 1 至少有一台linux环境,当前验证环境是Centos 8。
2 了解常见的linux脚本的写法,能认识或者常用单词的缩写。
3 本文会根据实际实践更新和修正,欢迎批评、指导
sed '[address-range|pattern-range] s/original-string/replacement-string/[substitute-flags]' inputfile
如下是使用的示例文本数据,这里的行号是为了方便解释结果,不是文本内容。
1 7369,smith,clerk,7902,'1980-12-17',800,null,20
2 7499,allen,salesman,7698,'1981-2-20',1600,300,30
3 7521,ward,salesman,7698,'1981-2-22',1250,500,30
4 7566,jones,manager,7839,'1981-4-2',2975,null,20
5 7654,martin,salesman,7698,'1981-9-28',1250,1400,30
6 7655,jack,manager,7698,'1987-3-28',1600,1800,10
7 7656,tim,clerk,7902,'1982-12-12',1400,1400,30
8 7657,kate,clerk,7902,'1989-11-11',1400,1800,10
9 7698,blake,manager,7839,'1981-5-1',2850,null,30
10 7699,dlake,salesman,7839,'1983-6-15',3000,null,10
11 7782,clark,manager,7839,'1981-1-9',2450,null,10
12 7788,scott,analyst,7566,'1982-12-9,3000,null,20
13 7839,king,president,null,'1981-11-17',5000,null,10
14 7844,turner,salesman,7698,'1981-12-8',1500,0,30
15 7876,adams,clerk,7788,'1983-1-12',1100,null,20
16 7900,james,clerk,7698,'1981-12-3',950,null,30
17
18 --7902,ford,analyst,7566,'1981-12-3',3000,null,20
19
20 7934,miller,clerk,7782,'1982-1-23',1300,null,10
# 1 用word2替换word1,该方式只会替换word1首次出现。
sed 's/word1/word2/' filename
# 示例 用null替换unkown
sed 's/null/unknow/' emp.txt
#以下内容仅摘录更改部分的数据,完整的数据未附录。
# 2 用word2替换word1,该方式只会替换word1首次出现同时忽略大小写。
sed 's/word1/word2/i' filename
# 示例 用null替换unkown
sed 's/null/unknow/i' emp.txt
# 3 用word2替换word1,g参数会在word1出现的所有位置替换。
sed 's/word1/word2/g' filename
# 示例 用mg替换manager
sed 's/null/unkown/g' emp.txt
# 对比示例1的结果不难发现,加了g选项后,该行里的所有的null都被替换成了unknow,而不是仅仅第一次出现的null。
# 4 将每行里第m此出现的word1替换为word2。
# 这里n的范围是1到512。
# 如果word1的出现次数小于m,那么替换不生效。
sed 's/word1/word2/m' filename
# 示例 用将每行里第二次出现的null替换为unkonw.
sed 's/null/unkown/2' emp.txt
#替换部分的结果见下:
7839,king,president,null,'1981-11-17',5000,unkown,10
# 5 对文件内出现word1关键字的行里的word2用word3替换。
sed '/word1/s/word2/word3/' filename
# 示例 将含有jack的行里的manager用mg替换
sed '/jack/s/manager/mg/' emp.txt
# 延展,同理如果word2出现多次,想要全部替换,想要加上g选项。即命令如:
sed '/jack/s/manager/mg/g' emp.txt
# 1删除第n行,以m为步长(增幅)的所有行,输出为剩下内容。这里~是波浪线。
sed 's/word1/word2/p' filename
# 示例 用将每行里第二次出现的null替换为unkonw并将结果打印在控制台上。
sed -n 's/null/unkown/2 p' emp.txt
sed -n 's/null/unkown/2p' emp.txt
# 结果见下:
7839,king,president,null,'1981-11-17',5000,unkown,10
# 2 文件infilename将关键字word1替换为word2并写入文件outfilename内。
sed '/word1/word2/w outfilename' infilename
# 删除包含clerk和salesman的行,这里主要是借助|(管道符号)实现,即前面的输出作sed -n 's/null/unknow/2w outfile.txt' emp.txt
# cat outfile.txt
7839,king,president,null,'1981-11-17',5000,unknow,10
# 3 替换结合e选项,即替换后的文本是linux可执行命令,加e之后可以直接执行输出结果。
# 文件filename_tmp.txt里有一行,内容是“emp”。
cat filename_tmp.txt
# 内容为emp
emp
# 通过e选项,可以直接执行命令。
sed 's/^/ls -a|grep /e' filename_tmp.txt
# 结果为
emp_bak.txt
emp.txt
# 将多个选项结合在一起使用。
# 示例,将每行里第2次出现的null替换为unkown,忽略大小写,全局替换(即所有出现2次时),显示在控制台里、写入文件outfile.txt内。
sed -n 's/null/unkonw/2gip w outfile.txt ' emp.txt
# 结果
7839,king,president,null,'1981-11-17',5000,unknow,10
#我们在字符串替换时用的符号是/,那么如果文本内容里也有/,那么显然是起冲突的。所以这是我们得用转移字符\。这和我们在其它语言里常见的处理方式类同。
# 将文件内的/替换为\,这里因为S的替换内含”/”了,所以想要用到转移字符”\”
cat filename_tmp.txt
# 文件内容见下:
/root/shenl/
# 执行转换
sed 's/\/root\/shenl\//\\root\\shenl\\/' filename_tmp.txt
# 结果,如下。
\root\shenl\
#如果嫌转义字符转来转去麻烦,也可以自定义替换里的分隔符,而不是用默认的”\”。
# 在替换命令s里自定义替换分隔符,这里用的@,也可以用|、!、^等特殊符号。前提是文本内不会出现该分隔符。
# 注意,这里指定的分隔符不支持多个字符,比如@|作为一个分隔符是不支持的。
# 将文件内的/替换为\
#cat filename_tmp.txt
# 文件内容
/root/shenl/
# 这里因为\是转义字符,所以想要加2个给转回来。
sed 's@/root/shenl/@\\root\\shenl\\@' filename_tmp.txt
# 结果
\rootshenl\
# 多个替换命令批量执行,执行时会按照顺序执行,而不是并行执行。
# 将sales替换为sale后再将man替换为woman。
sed '{
s/sales/sale/
s/man/woman/
}' emp.txt
# 如果想将几个匹配的模式打包成一个整体替换,可以用&
#以数字正则匹配,将4个数字的作为一个整体“word”,在这个“word”前后加上双引号“。
sed 's/^[0-9][0-9][0-9][0-9]/"&"/g' emp.txt
# 执行结果部分示例数据见下:
"7369",smith,clerk,7902,'1980-12-17',800,null,20
"7499",allen,salesman,7698,'1981-2-20',1600,300,30
"7521",ward,salesman,7698,'1981-2-22',1250,500,30
#如果想头和尾匹配则想要用到正则符号,^(头)、$(尾)。
# 示例,在每行的开头和结尾分别加上{和}。
sed 's/^.*$/{&}/g' emp.txt
# 结果示例
{7369,smith,clerk,7902,'1980-12-17',800,null,20}
#该方式对行按某个模式匹配,这里的模式可以认为是一种以字符串的分割或者前后有字符串的分割模式。比如以单字符;、|、:分割,或者已()、[]、{}前后分割。
# 这里的单组的意思是只提取出一组。:
cat file_tmp.txt
# 结果
/usr/apps/bin:/usr/local/bin:/root/:/root/software
/usr/local/sbin:/usr/apps/sbin:/opt/bin
# 提取出第一组
# 1 这里是\([^:]*\) 是匹配所有字符(内容)直到”:”。
# 2 \1指的是提取第1组。
sed 's/\(\/[^:]*\).*/\1/g' file_tmp.txt
# 结果
/usr/apps/bin
/usr/local/sbin
# 提取出第二组
# 参数类似上例,这里有个匹配模式后的符号“:”要指定正确。
sed 's/\([^:]*\):\([^:]*\):\(.*\).*/\2/g' file_tmp.txt
#结果
/usr/local/bin
/usr/apps/sbin
# 多组的和单组的类似,主要是输出时同对几个组同时操作(同组重复也可以)。
cat file_tmp.txt
#结果
/usr/apps/bin:/usr/local/bin:/root/
/usr/local/sbin:/usr/apps/sbin:/opt/bin
# 不难发现此时group1和group3调换了位置,且组间以逗号作为分隔符。
sed 's/\([^:]*\):\([^:]*\):\(.*\).*/\3,\2,\1/g' file_tmp.txt
#结果
/root/,/usr/local/bin,/usr/apps/bin
/opt/bin,/usr/apps/sbin,/usr/local/sbin
cat employee.txt
# 结果
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager
#对不同列进行操作并重新排序
sed 's/\([^,]*\),\([^,]*\),\(.*\).*/\3,\2,\1/g' employee.txt
#结果
CEO,John Doe,101
IT Manager,Jason Smith,102
Sysadmin,Raj Reddy,103
Developer,Anand Ram,104
Sales Manager,Jane Miller,105
# 替换每行结尾前2个字符为” new Append”(不含换行)。
sed 's/..$/,new Append/' emp.txt
#结果
7369,smith,clerk,7902,'1980-12-17',800,null,new Append
7499,allen,salesman,7698,'1981-2-20',1600,300,new Append
7521,ward,salesman,7698,'1981-2-22',1250,500,new Append
#原始数据
7369,smith,clerk,7902,'1980-12-17',800,null,20
7499,allen,salesman,7698,'1981-2-20',1600,300,30
7521,ward,salesman,7698,'1981-2-22',1250,500,30
#将salesman后的内容替换为空,即“删除”。这里它前面的一个字符也替换为空
# sed 's/.salesman.*//' emp.txt
# 结果
7369,smith,clerk,7902,'1980-12-17',800,null,20
7499,allen
7521,ward
# 原始文件(部分)
7369,smith,clerk,7902,'1980-12-17',800,null,20
7499,allen,salesman,7698,'1981-2-20',1600,300,30
7521,ward,salesman,7698,'1981-2-22',1250,500,30
#删除以”--”开头的行,--.*即匹配以—开头的所有行。
# 这里又用到多命令执行以”;”拼接,其中^$指的是空行。
sed -e 's/--.*//; /^$/ d' emp.txt
#删除以”--”结尾的行,.*--$即匹配以—结尾的所有行。
# 这里又用到多命令执行以”;”拼接,其中^$指的是空行。
sed -e 's/.*--$//; /^$/ d' emp.txt
#众所周知,Windows(DOS)的行分隔符是回车(CR)换行(LF),而Unix(Linux)的则是(换行)LF。所以如果想转换即将每行的倒数第一个(不含LF)的CR替换为空即可。
sed 's/.$//' emp.txt
# 1 将scoot关键字替换为SCoTt,这里o和t前分别加了\l标识,可认为做了转义,即不做大写处理。
sed -n 's/scott/SC\lOT\lT/p' emp.txt
#以下内容仅摘录更改部分的数据,完整的数据未附录。
7788,SCoTt,analyst,7566,'1982-12-9,3000,null,20,scott
# 原始文本内容
7788,scott,analyst,7566,'1982-12-9,3000,null,20,scott
# 2 将scoot关键字替换为Scott,这里尽管替换时写的是”OTT”但是前面加了\L标识,所以全部都变成了小写的ott。
sed -n 's/scott/SC\LOTT/p' emp.txt
# 执行结果,ott全是小写,尽管替换是是“OTT”
7788,SCott,analyst,7566,'1982-12-9,3000,null,20,scott
注:1 \L和\l(即大写L和小写l的区别在于l只能控制后面的1个字符为小写,而L则是后面所有的字符都是小写)。
2 不论\l还是\L都只是对匹配到的第一个关键字起作用,第二和之后的不起作用。
3 针对问题2,想对所有匹配的关键字起作用,想要加g选项(标识)。
# 3 将scoot关键字替换为SCoTt,这里o和t前分别加了\l标识,可认为做了转义,即不做大写处理。
sed -n 's/scott/sc\uot\ut/p' emp.txt
#以下内容仅摘录更改部分的数据,完整的数据未附录。
7788,scOtT,analyst,7566,'1982-12-9,3000,null,20,scott
# 原始文本内容
7788,scott,analyst,7566,'1982-12-9,3000,null,20,scott
# 4 将scoot关键字替换为Scott,这里尽管替换时写的是”OTT”但是前面加了\L标识,所以全部都变成了小写的ott。
sed -n 's/scott/sc\Uott/p' emp.txt
# 执行结果,OTT全是大写,尽管替换是小写“ott”
7788,scOTT,analyst,7566,'1982-12-9,3000,null,20,scott
注:1 特点和上节类似,这里\l、\u和
\U、\L可以一同记忆,即尽管在sed里输入的是大小写,但经过这些转换标识后会做相应的小大写转换。
2 这里l即low,u即是UPPER。
# 5 使用\U或者\L时如果想后面有些部分保留小写或者大写时可用\E标识。
# 示例 将scott替换为scOTT king
sed -n 's/scott/sc\Uott \Eking/p' emp.txt
如果不加\E,则结果会全部替换为
#sed -n 's/scott/sc\Uott king/p' emp.txt
7788,scOTT KING,analyst,7566,'1982-12-9,3000,null,20,scott