shell(3)--sed

sed  
本身是一个管线命令,可以进行数据的取代,删除,新增, 撷取特定行。

基本用法:

sed [-nefr] [动作]
选项:
-n  所有来自STDIN 的数据会被输出到屏幕,加上-n后,只有经过sed 处理的那一行数据(动作)才会被列出来
-e 直接在指令行模式上进行sed 的动作编辑
-f 直接将sed 的动作写在一个档案内,-f filename 则可以执行filename内的sed动作
-r sed 的动作支持的是延伸正规表示法的语法。(默认是基础正规表示法)
-i 直接修改读取的档案内容,不是输出到屏幕
动作:[n1[,n2]] function
n1,n2 表示选择进行动作的行数,一般不会存在。如要在10到20行进行 10,20 function
function 有以下选项:
a   新增,a 的后面接字符串,而这些字符串会在新的一行出现。(当前的下一行)
c   取代,c 后面接字符串,n1,n2之间的行被取代
d   删除,d后面通常不接任何东西
i    插入,i后面接字符串,插入到当前行的上一行。
p  打印,将某个选择的数据打印出来。通常与参数 sed -n 一起使用
s  取代,直接进行取代,如vi替换命令:1,20s/old/new/g 
y  转换命令
w 写入文件

准备示例文件
$cat last > last.log 

删除
删除 last.log  的2到5行
$cat last.log  -n | sed '2,5d'
删除第2行
$cat last.log  -n | sed '2d'
删除第2往后的所有行
$cat last.log  -n | sed '2,$d'

新增
在第2行后面加上‘aaabbb’ 行
$cat last.log  -n | sed '2a aaabbb'
如果要加在第2行前面,则使用 i 这个动作
$cat last.log  -n | sed '2i aaabbb'
添加多行
$cat last.log  -n | sed '2i aaabbb\ 回车换行后继续输入,最后输入 ‘ (单引号) 后再回车。如:
[jamin@localhost ~]$ cat last.log  -n |sed '2a aaa\
> bbb\
> ccc'

取代
2到5行替换为:aaabbbb
$ cat -n last.log  | sed '2,5c aaabbb'

提取(打印)
只显示2到5行
$ cat -n last.log  | sed -n '2,5p'

打印行号(结果只取了一个,会把行号写在行的上面):
$ sed "=" last.log
61
reboot   system boot  2.6.32-642.el6.x Thu Jan 12 06:47 - 08:13  (01:26) 

列出行
列出(list)命令(l)可以打印数据流中的文本和不可打印的ASCII字符。任何不可打印字符要么在其八进制值前加一个反斜线,要么使用标准C风格的命名法。(下面命令,只取了一行)
$ sed "=" last.log
reboot   system boot  2.6.32-642.el6.x Thu Jan 12 06:47 - 08:13  (01:\
26)    $

行搜索与替换

sed 's/old/new/flags' file, 类似vi 中是一样的。
flags 选项:
数字n     第n处匹配的将被替换。注:sed是行处理,所以行来计算的。如:n=2 将替换第2个匹配的
g         替换所有的
p         将原先的内容先打印出来
w file    将替换的结果保存在文件中(只保存发生替换后的行)

示例
取当前机器的ip
$  ifconfig eth0 | sed -n '2p' | sed 's/^.*addr://'| sed 's/Bcast:.*//g'
分析 ifconfig eth0 | sed -n '2p'  是取ip 的那一行,后面是执行替换
删除 cat /etc/man.config |grep 'MAN'  注释行:
$ cat /etc/man.config |grep 'MAN'| sed '/^#.*$/d'
只留下注释行
$cat /etc/man.config |grep 'MAN'| sed '/^#.*$/ p' -n
只留下注释行,grep  也可以
$ cat /etc/man.config |grep 'MAN' | grep '^#'

以上示例修改后的内容直接输出的屏幕的,如果想直接在原文件上修改,添加选项 -i。如:
$ sed '2,5c xxxxxx' -i last.log

选择行:

上面的命令是用数字,选择处理的行,表示行数(从1开始计数)的区间。sed 前面的选择行也支持正则表达式
示例:
查看包含jamin的行,把bash 替换成csh
$sed '/jamin/s/bash/csh/' /etc/passwd
删除/etc/passwd 文件中 root 用户的行
$sed '/root/d' /etc/passwd 

一次执行多个命令使用 -e 选项
命令之间用分号分开(也可以使用enter 键分开)
示例:
删除第二行,第9行前插入 test 字符
$cat -n last.log | sed '2d;9a test'
第二种写法(注意换行时不用加转义字符 \ )
$cat -n last.log | sed '2d
> 9a test'
第三种写法:
$cat -n last.log | sed -e '2d' -e '9a test'
第四种写法:(使用 { } ,删除2和9 两行,试过不能新增(a、i)和取代(c))
$cat -n last.log | sed  '{2d
> 9d}'

从文件中执行sed 命令
把sed 命令写在文件中,从文件中读取sed命令执行,使用-f 参数,适用于大量sed命令处理文件的情况。
示例:
script.sed 文件中写入sed 命令(删除第二行,在第9行插入test):
2d
9a test
$sed -f script.sed last.log

特殊字符处理,要使用 \ 转义字符,也可以使用第二种方式(编辑器允许选择其他字符来作为替换命令中的字符串分隔符)
示例:把/bin/bash 替换成 /bin/csh,下面三种写法一样的
$sed 's/\/bin\/bash/\/bash\/csh/g' /etc/passwd
$sed 's!/bin/bash!/bash/csh!g' /etc/passwd
$sed 's-/bin/bash-/bash/csh-g' /etc/passwd

转换命令
处理单个字符,不能限定字符出现的位置
示例:把 last.log 中的 r 替换成x  0 替换成y   t 替换成z。如果两边字符不一样长,则报错
#sed 'y/rot/xyz/' last.log

写入文件
示例:把/etc/passwd 文件的1,2两行写入sed.log 文件
# sed '1,2w sed.log' /etc/passwd
把包含root 行写入sed.root 文件( -n 不显示数据到屏幕)
# sed -n '/root/w root.sed' /etc/passwd 

从文件中读取
r 命令允许将文件中数据插入到数据流中
示例
将 last.log 文件数据,插入到sed.log 中的第一行之后,输出到屏幕。
$sed '1r last.log' passwd.log 
注意不是:
$sed '1ar last.log' passwd.log  #此操作是把r last.log 当着字符串插入到passwd.log 第一行之后了
在文件最后添加,直接使用$ 符号就可以了
# sed '$r num.sh' sed.log 

示例:
利用一个文件中的数据,替换文件中的点位文本
root.sed 文本如下:
this is test text
LIST
this is another test

把文件中LIST 用 /etc/passwd 中的行替换,并删除LIST
$sed '/LIST/{ 
r /etc/passwd
d
}' root.sed 


进阶:

next命令(单行next 命令 n)
通常sed编辑器在移动到数据流中的下一文本行之前,会在当前行上执行完所有定义好的命令。单行next命令改变了这个流程。
n 命令会将数据流中的下一文本行移到到sed编辑器的工作空间(模式空间),而不用重新回到命令的最开始再执行一遍。
示例:
有一个文件如下:
this is first

this is second

this is third

要求:删除包含first第一行后面的行:
$sed '/first/{n;d}' passwd.log 
说明:过滤包含 first 的行,把处理的数据流指向下一行(类似一个处理游标),执行d命令。

sed 多行处理
N  将数据流中的下一行加进来创建一个多组处理(把下一行合并到上一行,也就是当前处理的行)
D  删除多行组中的一行
P  打印多行组中的一行

N:把下一行合并到上一行,也就是当前处理的行
示例:
$last | sed '/jamin/{N;s/\n/ /}'
说明:查找包含jamin 的行,N 命令把下一行合并到当前行,把换行符(\n) 替换成空格

如果查找一个短语,分在了两行,则可以使用N 命令
示例:
test 文件内容如下(System Administrator 换行了):
On Tuesday, the Linux System
Administrator's group meeting will be held.
All System Administrators should attend.
----
On Tuesday, the Linux System Administrator's group meeting will be held.
On Tuesday, the Linux System
Administrator's group meeting will be held.
All System Administrator's should attend.
All System Administrator's should attend.

把System Administrator替换成 Desktop User
$ sed 'N ; s/System Administrator/Desktop User/' test                    1
$ sed 'N ; s/System.Administrator/Desktop User/' test                    2
$ sed 'N ; s/System\nAdministrator/Desktop\nUser/;s/System Administrator/Desktop User/' test           3
$ sed 's/System Administrator/Desktop User/;N ; s/System\nAdministrator/Desktop\nUser/' test           4
$ sed 's/System Administrator/Desktop User/;N ;s/System Administrator/Desktop User/; s/System\nAdministrator/Desktop\nUser/' test   5
第 1 个命令,先把下一行数据和当前处理的行合并,然后进行替换(注意只匹配了空格,所以换行没有被替换)。但是第9行也没有被替换(将在后面说明)。
第 2 个命令,在替换的原字符上加了一个.(来匹配空格和换行),这样就把第一行的换行的System Administrator 也匹配到了。但是输出的内容把第一行和第二行合并了(N 命令的作用);所以就有了第三个命令。(注:偶数换行的没有被替换)
第 3 个命令合并后,分别匹配空格字符和换行字符(\n)。
第 4 个命令:执行前3个命令,可以看到test 文件的最后一行没有被替换,因为:没有其他行可读入到模式空间跟这行合并,所以N 命令会错过它。所有在要把单行的命令放在N命令之前。
第 5 个命令:第4个命令出现的问题:如果最后一行是偶数行,那么会和被上一行合并,从而不能匹配有空格分开的了,所以需要加上,替换空格的字符。
问题一:上面的示例替换不了第6行才换行的数据。如test文件:第6行和第7行有数据的换行。处理第一行数据后,N 命令就把第5,6行合并到第一行了,7,8行合并了。所以上面的命令不能处理偶数行换行的数据。
解决方法:
从第二行选择处理,处理完毕后,再处理从第一行处理
$sed '2,${s/System Administrator/Desktop User/;N ;s/System Administrator/Desktop User/; s/System\nAdministrator/Desktop\nUser/}' test | sed '{s/System Administrator/Desktop User/;N ;s/System Administrator/Desktop User/; s/System\nAdministrator/Desktop\nUser/}'

多行删除命令
前面的 d 命令是单行删除,当与N 配合使用时要小心,可能删除掉两行数据。
示例:
还是使用N 命令时的 test 文件。如下命令会把合并成两行的数据都删除。
$sed 'N;/System\nAdminist/d' test
D : 多行删除命令,只删除模式空间中的第一行。
示例:把匹配到的第一行删除(如果需要删除的内容在第一行才能使用得上)
$sed 'N;/System\nAdminist/D' test

多行提取(打印)
P 只打印多行模式空间中的第一行
示例:匹配后,打印第一行内容
$sed -n 'N;/System\nAdminist/P' test


保持空间(hold space) 和 模式空间(pattern)
模式空间(pattern):是一块活跃的缓冲区,默认的是一个空行。在sed编辑器执行命令时它会保存待检查的文本。
保持空间:在处理模式空间中的某些行时,可以使用保持空间临时保存一些行。
有下面的命令用来操作保持空间:
h        将模式空间复制到保持空间(会替换保持空间的文本)
H        将模式空间附加到保持空间
g        将保持空间复制到模式空间(会替换别模式空间中的文本)
G        将保持空间附加到模式空间
x        交换保持空间和模式空间的内容
这些命令用来清空模式空间来加载其它要处理的字符串。
示例:
test 文件内容如下:
This is the test line.
This is the first data line.
This is the second data line.
This is the last line.

$sed -n '/first/ {h ; p ; n ; p ; g ; p }' test
打印结果:
This is the first data line.
This is the second data line.
This is the first data line.
分析:
1、匹配first 行
2、h 命令,把匹配到的行复制到hold space,接着p 命令,打印此行;所以打印出 first 这一行
3、接着n 命令,把下一行数据移动到模式空间,再执行 p 命令,所以打印的就是 second 这一行
4、再接着是g 命令,把hold space 复制到pattern space,执行p 命令,所以打印出最后一行

排除命令 !
示例:
test 文件内容如下:
This is the test line.
This is the first data line.
This is the second data line.
This is the last line.

$sed  -n '/test/!p' test
上面的命令如果不加排除命令! , 就打印匹配到 test 的那一行。加上! 后,情况就相反了,当匹配到test
行后,不执行p 命令,所以就把没有匹配的行都打印出来了。
在"sed 多行处理" 中,有个问题是不替换最后一行的数据,也可以使用! 来实现:
$sed 's/System Administrator/Desktop User/;N; s/System\nAdministrator/Desktop\nUser/' test  之前的处理方式
$sed '{$!N;s/System Administrator/Desktop User/;s/System\nAdministrator/Desktop\nUser/}' test
说明:$表明是最后一行,如果是最后一行则不执行N 命令。

改变流(分支,测试)
通常sed 会从脚本的顶部开始,一直执行到脚本的结尾;sed编辑器提供了一个方法来改变命令脚本执行的流程;结果和结构化编程类似。
改变流--分支
语法如下:[address]b [label]
address 参数决定了哪些行的数据会触发分支命令,label 是要跳转到命令的位置;如果没有label ,会跳转到命令行结尾。
示例1:
test 文件内容如下:
This is the test line.
This is the first data line.
This is the second data line.
This is the last line.
把This is 替换成Is this,路过第二行和第三行,line. 替换成test? 
$sed '{2,3b;s/This is/Is this/;s/line./test?/}' test
说明:分支命令中,第二行和第三行跳过了后面的替换命令。所以只有第一行和最后一行被替换了。
示例2:
示例1 是直跳到了命令行的末尾。可以使用label,定义跳转到的标签。标签以冒号开始,最后7个字符。
$ sed '/first/b jump;s/This is/No jump/; :jump s/This is/Jump to/' test
说明:行匹配到first时,就跳转到 :jump 位置执行命令;如果没有匹配,则顺序执行。执行结果:
No jump the test line.
Jump to the first data line.   #匹配到first 的行
No jump the second data line.
No jump the last line.

示例3:
$ sed '/first/b jump;s/This is/No jump/; :jump s/This is/Jump to/;s/line./test.../' test
说明:在上面的基础上,把line. 也替换。但是这个替换不是在 :jump 标签里面。行没有匹配到first 时,会顺序执行,但是不会执行 :jump 后面的命令。所以,在示例2的基础上,把所有的line. 替换成了 test...
问题::label 后面如何写多个命令?

示例4:
上面的示例都跳转到命令中后面的标签,也可以把标签定义到前面,这样就达到了循环的效果。
$ echo 'this, is, a, test, to, remove, commas.' | sed -n '{:start s/,//1p;b start}'
this is, a, test, to, remove, commas.
this is a, test, to, remove, commas.
this is a test, to, remove, commas.
this is a test to, remove, commas.
this is a test to remove, commas.
this is a test to remove commas.
上面的命令,只有ctrl+c 才会结束,处理这个问题的命令如下,只有匹配到‘,’才跳转到start 标签
$ echo 'this, is, a, test, to, remove, commas.' | sed -n '{:start s/,//1p;/,/b start}'

改变流--测试
测试命令t,类似分支命令b,不过b是根据地址跳转到分支,而测试命令t 是根据替换命令的结果跳转到某个标签。
语法:
[address]t [label]
如果替换命令成功匹配并替换了一个模式,测试命令则会跳转到指定的标签,如果没有指定标签则跳转到脚本结尾。如果未能匹配指定的模式,则不跳转,顺序执行脚本(不执行标签内的命令)。
示例:
test 文件内容如下:
This is the test line.
This is the first data line.
This is the second data line.
This is the last line.

$ sed '{s/fist/match/
> t;s/This is the/Not match/;
> }' test
注:t不能和替换命令写的一行。

示例:使用t命令实现循环替换
$ echo 'this, is, a, test, to, remove, commas.' | sed -n '{:start s/,//1p;
> s/,//1p
> t start}' 

模式替代
&符号,是指匹配到的字符。

示例:
$ echo "The cat sleeps in his hat." | sed 's/.at/"&"/g'
说明: & 就是前面 .at 匹配到的字符串,匹配到什么,& 就是什么。
替代单独的单词
& 符号提取匹配替换命令中指定模式的整个字符串。
圆括号()可以提取这个字符串的一部分。
示例:
$ echo "The System Administrator manual" | sed 's/\(System\) Administrator/\1 user/'
The System user manual
说明:
1、子模式要用(),并且要转义
2、替代字符串中的\1 是指的() 包裹的第一个子模式的字符串,这里是System。类推\2,\3 分别是第2个,第3个子模式。
3、子模式可以使用特殊字符来引用,那么替代字符就是这个匹配到的字符串
使用场景:需要用一个单词来替换一个短语,这个单词又是这个短语的一部分,这个时候就特别方便。如:
$ echo "That furry cat is pretty" | sed 's/furry \(.at\)/\1/'
That cat is pretty
这个时候不能使用&,& 会替换整个匹配的模式。当在两个或多个子模式间插入文本时。
示例:(小括号我用的是中文的代替的,不然显示不出来)

$ echo '1234567' | sed '{:start 
s/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/
t start
}'

结果为:1,234,567
说明:上面有两个匹配子模式 .*[0-9]和[0-9]{3}第一次匹配上的结果为 \1 是1234, \2 是567, 再依次类推。

在脚本中使用sed
1、把sed 命令写在shell 脚本中,可以将普通的shell 变量和sed编辑器一起使用。
示例:
sed.sh 脚本如下:
#!/bin/bash
sed '/first/b jump;s/This is/No jump/; :jump s/This is/Jump to/' $1
使用(test 是上面示例的文件):
$./sed.sh test


2、重定向sed 的输出
sed编辑器默认会将结果输出到STDOUT上。可以使用$() 将sed 编辑器的输出重定向到一个变量中,在后面脚本中使用
示例:
sed.sh 脚本如下:(使用 view plain 查看。其中\( 展示不出来


#!/bin/bash
num=$1
start=1
involution=1
for((;start<=num;start++))
do
    involution=$(($involution * $start))
done

result=$(echo $involution |sed '{
:start
s/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/
t start
}')
echo $result

说明:首先计算传入参数的阶乘,保存在involution 变量中,然后对结果3位数字插入一个逗号。sed 编辑器结果保存在result 变量中
如:
$./sed.sh 20
2,432,902,008,176,640,000

sed 实用工具
1、加行单距(第一行添加一个空行)。
$ sed '$!G' test
说明:
a- 使用上面的test 文件
b- 最后一行不执行G
c- 保持空间默认的值是一个空行,G 命令把保持空间附加到模式空间
如果test 文件中有多个空行的话,上面的命令就不能使用。首先要删除数据流中的
所有空白行,然后用G命令在所有行后插入新的空白行。
$ sed '{/^$/d;$!G}' test

2、给文件编行号(类似cat -n)
$ sed '=' test | sed '{N;s/\n/ /g}'
结果(前部分是添加了行号,后部分是把行号和内容合并为一行,用空格隔开。只取了一行结果)
1 This is the test line.

3、删除连续空白行
关键是找到空白行和不是空白行的地址区间,在此之外的行删除就行了。
$sed '/./,/^$/!d' test
说明:区间是/./到/^$/。区间的开始地址会匹配任何含有至少一个字符的行。区间的结束地址会匹配一个空行。在这个区间内的行不会被删除。

4、删除文件的开关空白行
$sed '/./,$!d' test
说明:这个区间从含有字符的行开始,一直到数据流结束。在这个区间内的任何行都不会从输出中删除。

5、删除结尾空白行
$ sed '{:start /^\n*$/{$d;N; b start}}' test
说明:上面命令花括号内还有一个花括号,是一个命令组。该命令组会被应用在指定的地址模式上。地址模式能够匹配只含有一个换行符的行。如果找到了这样的行,而且还是最后一行,删除命令会删掉它。如果不是最后一行, N命令会将下一行附加到它后面,分支命令会跳到循环起始位置重新开始。


猜你喜欢

转载自blog.csdn.net/convict_eva/article/details/73732544