: label 建立脚本内指令互相参考的位置。
# 建立解
{ } 集合有相同位址参数的指令。
! 不执行函数参数。
= 打印内容行数( line number )。
a 添加使用者输入的资料。
b label 将执行的指令跳至由 : 建立的参考位置。
c 以使用者输入的资料取代资料。
d 删除pattern space的内容。
D 删除pattern space内第一个newline字母前的内容。
g 将内容从hold space拷贝到pattern space(覆盖)
G 将内容从hold space拷贝添加到pattern space
h 将内容从pattern space拷贝到hold space(覆盖)
H 将内容从pattern space拷贝添加到hold space
l 打印文件中的 nonprinting character 用 ASCII 码。
i 插入添加使用者输入的资料行。
n 读入下一行。
N 添加下一行到pattern space。
p 打印pattern space的内容。
P 打印pattern space内第一个newline字母前的内容。
q 跳出sed编辑。
r 读入文件内容。
s 替换字串。
t label 先执行一替换的编辑指令,如果替换成功则将编辑指令跳至 : label 处执行。
w 写内容到文件。
x 交换hold space与pattern space的内容。
y 转换(transform)字元。
虽然sed只有上面这些拥有基本编辑功能的函数,但由指令中位址参数和指令与指令间的配合,能使sed 完成大部份的编辑任务。
sed是一种面向字符流的编辑器。处理文件时,sed把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾,一次只处理文件中一行的拷贝。文件内容并没有改变,除非你使用重定向存储输出。
sed同时维护着模式空间(pattern space)和保持空间(hold space),可以将模式空间的内容复制到保持空间并在以后检索它们。
调用sed命令有两种形式
sed [options] 'command' file(s)
sed [options] -f scriptfile file(s)
常用的options
-e command, --expression=command 允许多点编辑。
-n, --quiet, --silent 取消默认输出。
-f, --filer=script-file 引导sed脚本文件名
使用的基础command及示例
1、[address]s/pattern/replacement/flags ---substitution
用string替换正则表达式re。
以下的是替换标记flags
g表示行内全面替换。
p表示打印行。
w表示把行写入一个文件。
示例:sed -i 's/old/new/g' file GNU sed version 4.0.5以上带有-i选项
2、d [ address [,address ] ] d---delete
从模式空间(Pattern space)位置删除匹配行。
示例:
$sed '1d' quote.txt
It was an evening of splendid music and company.
Too bad the disco floor fell through at 23:00.
The local nurse Miss P.Neave was in attendance
$ sed '1,3d' quote.txt
The local nurse Miss P.Neave was in attendance.
$sed /^$/d 删除空行
3、追加 插入 更改
a ---append
在当前行后面加入一行文本。
i ---insert
在当前行上面插入文本。
c ---change
用新的文本改变本行的文本。它清除模式空间的内容,类似d命令的效果
append [line-address]a\
text
insert [line-address]i\
text
change [address]c\
text
4、l ---list
显示模式空间内容,将非打印的字符显示为两个数字的ascii码。
$ cat test/spchar
Here is a string of special characters: ^A ^B
^M ^G
$ sed -n -e "l" test/spchar
Here is a string of special characters: \01 \02
\15 \07
$ # test with GNU sed too
$ gsed -n -e "l" test/spchar
Here is a string of special characters: \01 \02
\r \a
5、y [address]y/abc/xyz/ ---Transform
按位置将abc中的每个字符转换成xyz中的
转换成大写
y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
6、p ---print
打印模式空间的行。
=
打印被匹配行的行号。
q [line-address]q---quit
退出sed。
7、n [address]n ---next
输出模式空间内容,然后读取下一个输入行,用下一个命令处理新的行而不是用第一个命令。不返回脚本顶端。
附录:sed正则表达式元字符集
^
锚定行的开始 如:/^sed/匹配所有以sed开头的行。
$
锚定行的结束 如:/sed$/匹配所有以sed结尾的行。
.
匹配一个非换行符的字符 如:/s.d/匹配s后接一个任意字符,然后是d。
*
匹配零或多个字符 如:/*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个o的行。
x\{m,\}
重复字符x,至少m次,如:/o\{5,\}/匹配至少有5个o的行。
x\{m,n\}
重复字符x,至少m次,不多于n次,如:/o\{5,10\}/匹配5--10个o的行。
sed基础二
sed的高级命令,按照《o’reilly sed and awk》分法,分成如下三组:(本文很多例子也出自这本书)
一 处理多行模式空间(N D P)
二 采用保持空间来保存模式空间的内容并使它可用于后续的命令(H h G g x)
三 使用分支和条件指令更改脚本的控制流(: b t)
sed脚本中,正常的控制流是:一行被读入模式空间并且用脚本的每个命令逐个地应用于那一行,当到达脚本的底部时,输出这一行并且清空模式空间,然后新行被读入模式空间,控制被转移到脚本的顶端。
在这里, 我们将改变脚本控制流,按照我们的预期来执行。
第一部分 N D P命令
N命令:
多行N命令通过读取新的输入行,并将它添加到模式空间的现有内容之后来创建多行模式空间。最初内容与新行直接用\n分隔,sed将它看作一个有很多嵌入行的整行。
小n命令区别在于它输出模式空间内容,然后读取新行,并不建立多行模式空间。
示例:
Consult Section 3.1 in the Owner and Operator
Guide for a description of the tape drives
available on your system.
我们现在想要将"Owner and Operator Guide"换成"Installation Guide",但它出现在文件的两行上,
/Operator$/{
N
s/Owner and Operator\nGuide/Installation Guide/
}
避免产生超长行 ,可以s/Owner and Operator\nGuide/Installation Guide\n/
实际上, Owner and Operator Guide可能在不同的位置上分成多行,我们可以修改正则,下面是一个完整的脚本:
s/Owner and Operator Guide/Installation Guide/
/Owner/{
$!N
s/ *\n/ /
s/Owner and Operator Guide */Installation Guide\
/
}
D命令:
删除命令D是删除命令d的多行形式,区别在于d删除模式空间的内容并读入新行,从而在脚本顶端重新使用编辑方法。
D只删除多行模式空间的第一个嵌入的换行符以前的内容,它不会导致读入新行,相反,它返回到脚本的顶端,将这些指令应用于模式空间剩余的内容。
示例:
# 将多个空行减少到一个空行,使用d命令版本
/^$/{
N
/^\n$/d
}
测试文件如下:
This line is followed by 1 blank line.
This line is followed by 2 blank lines.
This line is followed by 3 blank lines.
This line is followed by 4 blank lines.
This is the end.
运行脚本产生一行结果:
[icepp@fc8 test]$ sed -f sed.blank test.blank
This line is followed by 1 blank line.
This line is followed by 2 blank lines.
This line is followed by 3 blank lines.
This line is followed by 4 blank lines.
This is the end.
当有偶数个空行时,所有空行都被删掉。仅当有奇数行时,有一行被保留下来,这是因为d命令删除整个模式空间的内容,一旦遇到第一个空行,就读入下一行,并且两行都被删除。如果遇到第三行,并且下行不为空行,那么d命令就不会执行,因此空行被输出。
我们将d命令换成D命令,测试如下:
[icepp@fc8 test]$ sed -f sed.blank test.blank
This line is followed by 1 blank line.
This line is followed by 2 blank lines.
This line is followed by 3 blank lines.
This line is followed by 4 blank lines.
This is the end.
得到我们预期的结果。多行Delete的工作完成的原因:当遇到两个空行的时候,D命令只删除两个空行的第一个,下次遍历脚本的时候,将导致另一行被读入模式空间。如果那行不为空,那么两行都输出,因此确保了输出一个空行。也就是当模式空间有两个空行的时候,只有第一个空行被删除。当一个空行跟有文本的时候,模式空间的内容都输出。
P命令:
多行Print和小print稍有不同。它输出多行模式空间的第一部分,直到第一个嵌入的换行符。执行完脚本的最后一个命令后,模式空间的内容自动输出(-n选项将抑制这个默认的动作)。
因此,当默认输出被抑制或脚本的控制流更改,以至于不能到达脚本底部的时候,需要使用打印命令P或者p。Print命令经常出现在Next和Delete命令之前。这三个命令能够建立一个输入输出循环,用来维护两行的模式空间,但是一次只输出一行,然后返回到脚本的顶端将所有命令应用于模式空间的第二行。没这个循环,当执行到脚本的最后一个命令的时候,模式空间这两行将被输出。
示例:
测试文件
Here are examples of the UNIX
System. Where UNIX
System appears, it should be the UNIX
Operating System.
测试脚本
/UNIX$/{
N
/\nSystem/{
s// Operating &/
P
D
}
}
执行结果如下
[icepp@fc8 test]$ sed -f sed.Print test.Print
Here are examples of the UNIX Operating
System. Where UNIX Operating
System appears, it should be the UNIX
Operating System.
创建多行模式空间以匹配第一行结尾的“UNIX”和第二行的开始的“System”,如果发现“UNIX System”跨越两行,我们就将它变成“UNIX Operating System”。建立这个循环以返回到脚本顶端,并寻找第二行结尾的“UNIX”。
首先, Next将一个新行追加到模式空间的当前行。在替换命令应用与多行模式空间后,模式空间的第一部分被Print输出,然后被D命令删除。这样,当前行被输出并且新行成为当前行。D命令阻止到达脚本底部输出并清空模式空间的两行,并将控制转移到了脚本顶部,N命令重新将一个新行读入模式空间,这样就形成了循环。
第二部分 H h G g x命令
模式空间(pattern space)是容纳当前输入行的缓冲区。sed还使用一个称为保持空间(hold space)的预留(set-aside)缓冲区。
这部分的命令就是用于在模式空间和保留空间之间移动数据,保持空间用于临时存储,单独的命令不能寻址保持空间或者更改它的内容。
hold命令(H, h)将数据转移至保持空间。
get命令(G, g)将保持空间的数据移至模式空间。
小写字母命令改写目的缓存区的内容,而大写字母命令是追加缓存区的现有内容。
Exchange命令(x)交换保持空间和模式空间的内容。
示例1:反转行的顺序,颠倒1和2开始的行
样本文件
1
2
11
22
111
222
脚本文件
# Reverse flip
/1/{
h
d
}
/2/{
G
}
执行结果
[icepp@fc8 test]$ sed -f sed.flip test.flip
2
1
22
11
222
111
首先, 我们将第一行h复制到保持空间,然后d清除模式空间,sed将读入下一行,G命令将保持空间的内容追加append到模式空间。匹配“1”的行都被复制到保持空间并从模式空间删除,控制转移到脚本的顶端并且不打印那一行。我们保存这两行中的第一行并且直到匹配第二行时才输出它。
Hold 命令后面跟delete命令是一种常见的搭配。没有delete命令,控制将一直进行到脚本的底部,并且模式空间的内容将被输出。如果脚本中使用next(n)命令而不是delete命令,那么模式空间的内容也会被输出。
这仅仅是一个用于演示的逻辑很差的脚本。
示例2:
样本文件
find the Match statement
Consult the Get statement.
using the Read statement to retrieve data
我们将要完成
s/find the Match statement/find the MATCH statement/g
使用脚本
# 将语句的名字变成大写形式
/the .* statement/{
h
s/.*the \(.*\) statement.*/\1/
y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
G
s/\(.*\)\n\(.*the \).*\( statement.*\)/\2\1\3/
}
地址将过程限制在匹配the .* statement的行上
h hold命令将当前输入行复制到保持空间。使用样本行”find the Match statement”,我们来显示模式空间和保持空间的内容。应用h命令后:
pattern space: find the Match statement
hold space: find the Match statement
s/.*the \(.*\) statement.*/\1/
这个替换命令从行中取出语句的名字,并且用它来替代整个行
Pattern space: Match
Hold space : find the Match statement
y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
这个转换命令将每个小写字母换成大写字母。
pattern space: MATCH
hold space : find the Match statement
G Get命令将保持在保持空间的内容追加到模式空间
pattern space: MATCH\nfind the Match statement
hold space: find the Match statement
s/\(.*\)\n\(.*the \).*\( statement.*\)/\2\1\3/
这个替换命令匹配模式空间的3个部分: 1)嵌入的换行符之前的所有字符 2)从嵌入的换行符开始直到后面跟有一个空格的the,且包括the在内的所有字符 3)以空格并且后面跟有statement开始直到模式空间结尾的所有字符。
pattern space: find the MATCH statement
hold space : find the Match statement
运行脚本后的结果:
find the MATCH statement
Consult the GET statement.
using the READ statement to retrieve data
第三部分 分支和条件指令更改脚本的控制流(: b t)
分支branch和测试命令将脚本中的控制转移到包含特殊标签的行。如果没有指定标签,则控制转移到脚本结尾。分支branch命令用于无条件转移,测试test命令用于有条件转移。
标签是任意不多于7个字符的序列,本身以冒号开始并占据一行。
:mylabel
用法简单示例:
command1
/pattern/b end
command2
:end
command3
command1
/pattern/b dothree
command2
b
:dothree
command3
sed 的编辑指令均由位址(address)和函数(function)两部份组成 , 其中 , 在执行时 , sed 利用他的位址参数来
决定编辑的对象;而用他的函数参数(解[3])编辑。
此外 , sed 编辑指令 , 除了可在命令列上执行 , 也可在档案内执行。其中差别只是在命令列上执行时 , 其前必
须加上选项 -e ; 而在档案(解[4])内时 , 则只需在其档名前加上选项 -f。另外 , sed 执行编辑指令是依照他
们在命令列上或档内的次序。
执行 sed 输出控制。
2.2 sed 编辑指令
2.3 执行档案内的编辑指令
2.4 执行多个档案的编辑
2.5 执行 sed 输出控制
2.1.执行命令列上的编辑指令
当编辑指令(参照[section 2.2])在命令列上执行时 , 其前必须加上选项 -e 。其命令格式如下 :
sed -e '编辑指令1' -e '编辑指令2' ... 文档档
左而右。
馀文字中的 "yellow" 字串改成 "black" 字串。此时 , 可将编辑指令直接在命令上执行 , 其命令如下 :
"yellow" 字串替换(substuite)成 "black" 字串。
2.2 sed 的编辑指令
sed 编辑指令的格式如下 :
[address1[,address2]]function[argument]
数 function[argument] 为 sed 的内定函数 , 表示执行的编辑动作。
下面两小节 , 将仔细介绍位址参数的表示法和有哪些函数参数供选择。
实际上 , 位址参数表示法只是将要编辑的资料行 , 用他们的行数或其中的字串来代替表示他们。下面举几个例子
说明(指令都以函数参数 d(参照[section4.2]) 为例) :
删除档内第 10 行资料 , 则指令为 10d。
删除含有 "man" 字串的资料行时 , 则指令为 /man/d。
删除档内第 10 行到第 200 行资料, 则指令为 10,200d。
删除档内第 10 行到含 "man" 字串的资料行 , 则指令为 10,/man/d。
接下来 , 以位址参数的内容和其个数两点 , 完整说明指令中位址参数的表示法(同样也以函数参数 d 为例)。
位址参数的内容:
位址为十进位数字 : 此数字表示行数。当指令执行时 , 将对符合此行数的资料执行函数参数指示的编辑动作。例如 ,
删除资料档中的第 15 行资料 , 则指令为 15d(参照[section4.2])。其馀类推 ,如删除资料档中的第 m 行资料 , 则
指令为 md 。
当资料行中有符合 regular expression 所表示的字串时 , 则执行函数参数指示的编辑动作。另外 , 在
regular expression 前後必须加上 "/"。例如指令为 /t.*t/d , 表示删除任何含两 "t" 字母的资料行。其中 , "."
表示任意字元; "*" 表示其前字元可重任意次 , 他们结合 ".*" 表示两 "t" 字母间的任意字串。
参数时 , 表示只有符合位址的资料行才编辑 ; 当有两个位址参数 , 如 address1,address2 时 , 表示对资料区执行
编辑 , address1 代表起始资料行 , address2 代表结束资料行。对於上述内容 , 以下面例子做具说明。
例如指令为
例如指令为
例如指令为
2.2.2 有那些函数(function)参数
下页表中介绍任何 sed 的函数参数(参照[chapter 4])的功能。
函数参数 功能
: label 建立 script file 内指令互相参考的位置。
# 建立解
{ } 集合有相同位址参数的指令。
! 不执行函数参数。
= 印出资料行数( line number )。
a 添加使用者输入的资料。
b label 将执行的指令跳至由 : 建立的参考位置。
c 以使用者输入的资料取代资料。
d 删除资料。
D 删除 pattern space 内第一个 newline 字母 前的资料。
g 拷贝资料从 hold space。
G 添加资料从 hold space 至 pattern space 。
h 拷贝资料从 pattern space 至 hold space 。
H 添加资料从 pattern space 至 hold space 。
l 印出 l 资料中的 nonprinting character 用 ASCII 码。
i 插入添加使用者输入的资料行。
n 读入下一笔资料。
N 添加下一笔资料到 pattern space。
p 印出资料。
P 印出 pattern space 内第一个 newline 字母 前的资料。
q 跳出 sed 编辑。
r 读入他档内容。
s 替换字串。
t label 先执行一替换的编辑指令 , 假如替换成牛p>则将编辑指令跳至 : label 处执行。
w 写资料到他档内。
x 交换 hold space 和 pattern space 内容。
y 转换(transform)字元。
虽然 , sed 只有上表所述几个拥有基本编辑功能的函数 , 但由指令中位址参数和指令和指令间的配合 , 也能使 sed 完成大部份的编辑任务。
2.3 执行档案内的编辑指令
当执行的指令太多 , 在命令列上撰写起来十分混乱 , 此时 , 可将这些指令整理储存在档案
(譬如档名为 script_file )内 , 用选项 -f script_file , 则让 sed 执行 script_file 内的编辑指令。其命
令的格示如下 :
sed -f script_file 文档档
sed -f ysb.scr yel.dat
1,10d
s/yellow/black/g
档案内的指令 , 则由上而下执行。
2.4 执行多个文档档的编辑
在 sed 命令列上 , 一次可执行编辑多个文档档 , 他们跟在编辑指令之後。例如 , 替换
white.dat、red.dat、black.dat 档内的 "yellow" 字串成 "blue" , 其命令如下:
sed -e 's/yellow/blue/g' white.dat red.dat black.dat
进行字串的替换。
在命令列上的选项 -n (解[7]) 表示输出由编辑指令控制。由前章内容得知 , sed 会 "自动的" 将资料由
pattern space 输送到标准输出档。但藉着选项 -n , 可将 sed 这 "自动的" 的动作改成 "被动的" 由他所执行的
编辑指令(解[8])来决定结果是否输出。
由上述可知 , 选项 -n 必须和编辑指令一起配合 , 否则无法获得结果。例如 , 印出 white.dat 档内含有 "white"
字串的资料行 , 其命令如下:
移给编辑指令;/white/p 将资料行中含有 "white" 字串印出萤幕。
sed 'Command' filename(s) 只显示结果而不修改文件。
sed '/10[1-4]/d' file 显示文件file,除去包含101-104的行。
sed '2,$d' file 显示文件,只显示第一行。sed '2,$!d' file则只显示除第一行外的其它行。
sed '/^ *$/d file 删除文件中的空行。
2、sed -n '/10[1-4]/p' file 只显示文件file中包含101-104的行。(-n和p必须同时使用,否则只有p时显示全部文件并多显示一次找到的行)
sed -n '5p' file 只显示文件的第5行
3、sed 's/moding/moden/g' file 将moding替换为moden
4、sed -n 's/^west/north/p' file 将west开头的行替换为north并显示出来。
5、sed 's/[0-9][0-9][0-9]$/&.5/' file将file文件中以3个数字结尾的行替换为原数字加".5",&代表搜索到的字符串。
6、sed 's/\(mod\)ing/\1en/g file 将mod做为模式1封装在括号里,然后替换。
sed 's/...$//' file 删除每一行的最后三个字符。
sed 's/^...//' file 删除每一行的头三个字符。
7、sed 's#moding#moden#g' file 将moding替换为moden,s后面的#代表搜索串和替换串之间的分界符。
8、sed -n '/101/,/105/p' file 显示从101的匹配行到105的匹配行。如果只找到101的匹配行,则从101的匹配行到文件末。
sed -n '2,/999/p' file 显示从第2行到匹配行。
9、sed '/101/,/105/s/$/ 20050119/' file将从101的匹配行到105的匹配行的行末增加" 20050119"内容。
10、sed -e '1,3d' -e 's/moding/moden/g' file 先删除文件的1-3行,再进行替换。
sed -e '/^#/!d' file 显示文件以#开头的行。
11、sed '/101/r newfile' file 在每个匹配行增加文件newfile的内容
sed '/101/w newfile' file 把匹配行写入newfile。
12、sed '/101/a\
> ###' file 在匹配行后增加一新行。
sed '/101/i\
> ###' file 在匹配行前增加一新行。
sed '/101/c\
> ###' file 用新行替换匹配行。
14、sed '5q' file 显示到第5行时退出。
15、sed '/101/{ n; s/moding/moden/g; }' file 在文件中找到匹配行的后一行(n)再进行替换。
sed '/101/{ s/moding/moden/g; q; }' file 在文件中找到第一个匹配行后进行替换后再退出。
16、sed -e '/101/{ h; d; }' -e '/104/{ G; }' file 在文件中找到与101匹配行后先存在一个缓存中,再放在与104匹配行后。
sed -e '/101/{ h; d; }' -e '/104/{ g; }' file 在文件中找到与101匹配行后先存在一个缓存中,再替代104的匹配行。
sed -e '/101/h' -e '$G' file 将最后一个匹配行放在文件末。
sed -e '/101/h' -e '$g' file 将最后一个匹配行替换文件末行。
sed -e '/101/h' -e '/104/x' file 在文件中找到与101匹配行后先存在一个缓存中,再与104的匹配行进行互换。
17、sed -f sfile file 根据文件sfile的命令列表进行操作。
cat sfile
/101/a\
####101####\
****101****
/104/c\
####104 deleted####\
****104 deleted****
1i\
####test####\
****test****
处理都是在这个缓存中进行的。这对接下来的学习是有帮助的。
在正常情况下,sed将待处理的行读入模式空间,脚本中的命令就一条接着一条的对该行
进行处理,直到脚本执行完毕,然后该行被输出,模式空间清空;然后重复刚才的动作,
文件中的新的一行被读入,直到文件处理完备。
但是,各种各样的原因,比如用户希望在某个条件下脚本中的某个命令被执行,或者希望
模式空间得到保留以便下一次的处理,都有可能使得sed在处理文件的时候不按照正常的
流程来进行。这个时候,sed设置了一些高级命令来满足用户的要求。
总的来说,这些命令可以划分为以下三类:
1. N、D、P:处理多行模式空间的问题;
2. H、h、G、g、x:将模式空间的内容放入存储空间以便接下来的编辑;
3. :、b、t:在脚本中实现分支与条件结构。
多行模式空间的处理:
由于正则表达式是面向行的,因此,如若某个词组一部分位于某行的结尾,另外一部分又
在下一行的开始,这个时候用grep等命令来处理就相当的困难。然而,借助于sed的多行
命令N、D、P,却可以轻易地完成这个任务。
多行Next(N)命令是相对于next(n)命令的,后者将模式空间中的内容输出,然后把下一行
读入模式空间,但是脚本并不会转移到开始而是从当前的n 命令之后开始执行;而前者则
保存原来模式空间中的内容,再把新的一行读入,两者之间依靠一个换行符"\n"来分隔。
在N命令执行后,控制流将继续用N命令以后的命令对模式空间进行处理。
值得注意的是,在多行模式中,特殊字符"^"和"$"匹配的是模式空间的最开始与最末尾,
而不是内嵌"\n"的开始与末尾。
例1:
$ cat expl.1
Consult Section 3.1 in the Owner and Operator
Guide for a description of the tape drives
available on your system.
现在要将"Owner and Operator Guide"替换为"Installation Guide":
$ sed '/Operator$/{
> N
> s/Owner and Operator\nGuide/Installation Guide\
> /
> }' expl.1
在上面的例子中要注意的是,行与行之间存在内嵌的换行符;另外在用于替代的内容中要
插入换行符的话,要用如上的"\"的转义。
再看一个例子:
例2:
$ cat expl.2
Consult Section 3.1 in the Owner and Operator
Guide for a description of the tape drives
available on your system.
Operator Guide and the User Guide.
$ sed 's/Owner and Operator Guide/Installation Guide/
> /Owner/{
> N
> s/ *\n/ /
> s/Owner and Operator Guide */Installation Guide\
> /
}' expl.2
结果得到:
Consult Section 3.1 in the Installation Guide
for a description of the tape drives
available on your system.
and the User Guide.
看上去sed命令中作了两次替换是多余的。实际上,如果去掉第一次替换,再运行脚本,
就会发现输出存在两个问题。一个是结果中最后一行不会被替换(在某些版本的sed中甚至
不会被输出)。这是因为最后一行匹配了"Owner",执行N命令,但是已经到了文件末尾,某
些版本就会直接打印这行再退出,而另外一些版本则是不作出打印立即退出。对于这个问
题可以通过命令"$!N"来解决。这表示N命令对最后一行不起作用。另外一个问题是"look
manuals"一段被拆为两行,而且与下一段的空行被删除了。这是因为内嵌的换行符被替换
的结果。因此,sed中做两次替换一点也不是多余的。
例3:
$ cat expl.3
<para>
in a paragr
aph. Yet another.
100001000100100010001000001000000000000000000000000000000000000
000000
These lines should print.
我们的sed命令是这样的:
$ sed '/<para>/{
> N
> c\
> .LP
> }
> /<Figure Begin>/,/<Figure End>/{
> w fig.interleaf
> /<Figure End>/i\
> .FG\
> <insert figure here>\
> .FE
> d
> }
> /^$/d' expl.3
运行后得到的结果是:
.LP
This is a test paragraph in Interleaf style ASCII. Another line
in a paragraph. Yet another.
.FG
<insert figure here>
.FE
.LP
More lines of text to e found after the figure.
These lines should print.
而<Figure Begin>与<Figure End>之间的内容则写入文件"fig.interleaf"。值得注意的
是命令"d"并不会影响命令i插入的内容。
命令"d"作用是删除模式空间的内容,然后读入新的行,sed脚本从头再次开始执行。而命
令"D"的不同之处在于它删除的是直到第一个内嵌换行符为止的模式空间的一部分,但是
不会读入新的行,脚本将回到开始对剩下内容进行处理。
例4:
$ cat expl.4
This line is followed by 1 blank line.
This line is followed by 3 blank line.
This is the end.
不同的删除命令获得不同的结果:
$ sed '/^$/{ $ sed '/^$/{
> N > N
> /^\n$/d > /^\n$/D
> }' expl.4 > }' expl.4
-sh-3.1$ sed '/^$/{
> N
> /^\n$/d
> }' sedad4
This line is followed by 1 blank line.
This line is followed by 3 blank line.
This is the end.
-sh-3.1$ sed '/^$/{
N
/^\n$/D
}' sedad4
This line is followed by 1 blank line.
sed对文件中每一行(不管处理与否)的默认动作是将其输出,如果加上选项"-n",则输出
动作会被抑制,这时还希望输出就需要打印命令。单行模式空间的打印命令是"p",多行
模式空间的打印命令是"P"。P命令打印的是模式空间中直到第一个内嵌换行符为止的一部
分。
P 命令通常出现在N命令之后D命令之前,由此构成一个输入输出循环。在这种情况下,模
式空间中始终存在两行文本,而输出始终是一行文本。使用这种循环的目的在于输出模式
空间中的第一行,然后脚本回到起始处,再对空间中的第二行进行处理。设想一下,如果
没有这个循环,当脚本执行完备,模式空间中的内容都会被输出,可能就不符合使用者的
要求或者降低了程序执行的效率。
下面是一个例子:
例5:
$ cat expl.5
Here are examples of the UNIX
System. Where UNIX
System appears, it should be the UNIX
Operating System.
$ sed '/UNIX$/{
> N
> /\nSystem/{
> s// Operating &/
> P
> D
> }
> }' expl.5
替换的结果是:
Here are examples of the UNIX Operating
System. Where UNIX Operating
System appears, it should be the UNIX
Operating System.
可以将sed命令中的"P"、"D"换作小写,比较一下两种类型的命令的不同之处。
下面的例子就有相当的难度了:
例6:
$ cat expl.6
I want to see @fl(what will happen) if we put the
font change commands @fl(on a set of lines). If I understand
things (correctly), the @fl(thir
d) line causes problems. (No?).
Is this really the case, or is it (maybe) just something else?
well as one that begins on one line and ends @fl(somewhere
on another line). What if @fl(it is here) on the line?
Another @fl(one).
现在要作的就是将"fl@(…)替换为"\fB(…)\fR。以下就是满足条件的sed命令:
$ sed 's/@fl(\([^)]*\))/\\fB\1\\fR/g
> /@fl(.*/{
> N
> s/@fl(\(.*\n[^)]*\))/\\fB\1\\fR/g
> P
> D
> }' expl.6
然而,如果不使用这种输入输出循环,而是单单用N来实现的话,就会出现问题:
$ sed 's/@fl(\([^)]*\))/\\fB\1\\fR/g
> /@fl(.*/{
> N
> s/@fl(\(.*\n[^)]*\))/\\fB\1\\fR/g
> }' expl.6
这样的sed脚本是有漏洞的。
对行进行存储:
前面已经解释了模式空间的定义,而在sed中还有一个缓存叫作存储空间。在模式空间和
存储空间中的内容可以通过一组命令互相拷贝:
命令 简写 功能
Hold h或H 将模式空间的内容拷贝或附加到存储空间
Get g或G 将存储空间的内容拷贝或附加到模式空间
Exchange x 交换模式空间和存储空间中的内容
命令的大小写的区别在于大写的命令是将源空间的内容附加到目标空间,而小写的命令则
是用源空间的内容覆盖目标空间。值得注意的是,不管是Hold命令还是Get命令,都会在
目的空间的原有内容之后加上一个换行符,然后才把源空间中的内容加到换行符的后
面。
从下面这个例子,可以体会这部分内容的初步应用:
例7:
$ cat expl.7
1
2
11
22
111
222
我们要做的工作就是将第一行与第二行,第三行与第四行,第五行与第六行互换。sed的
命令各式是:
$ sed '
> /1/{
> h
> d
> }
> /2/{
> G
> }' expl.7
这个过程是这样的:首先,sed将第一行读入模式空间,然后h命令将其放入存储空间保存
起来,一个d命令又把模式空间中的内容清空;接着sed把第二行读入模式空间,然后G命
令把存储空间中的内容附加到模式空间(注意的是在模式空间的原内容末尾是加了一个换
行符的)。
最后得到的结果如下:
2
1
22
11
222
111
使用H或h命令的时候,比较常见的是在这个命令之后加上d命令,这样一来,sed脚本不会
到达最后,因而模式空间中的内容也就不会输出了。另外,如果把d换作n,或者把G换作
g,都不会达到目的的。
子母的大小写转换什么最方便,估计是tr了。
$ tr "[a-z]" "[A-Z]" File
很利害的是sed也可以完成这个转换。相应的命令是y:
$ sed '
> /[address]/y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' File
然而y命令是对整个行完全进行修改,因此如果只是将行里面的几个字符变换大小写的
话,这样做是行不通的。为完成这个工作,需要借助上面刚提到的Hold和Get命令了。
cat expl.8
find the Match statement
Consult the Get statement
using the Read statement to retrieve data
$ sed '/the .* statement/{
> h
> s/.*the \(.*\) statement.*/\1/
> y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
> G
> s/\(.*\)\n\(.*the \).*\( statement.*\)/\2\1\3/
> }' expl.8
以第一行的处理过程来说明这段命令的含意:
(1) "find the Match statement"被放入存储空间;
(2) 替换改行得到:Match;
(3) 将(2)的结果转换为大写:MATCH;
(4) 从存储空间去处(1)保留的内容附加到模式空间,此时模式空间的内容为:
MATCH\nfind the Match statement
(5) 再次对模式空间的内容替换得到:find the MATCH statement。
下面将举到的例子要用到比较扎实的正则表达式,不过没有关系,慢慢来,一切问题都是
可以解决的。另外这个例子用到的文本主要是和编辑排版有关的,这
例9:
$ cat expl.9.sed
h
s/[][\\*.]/\\&/g
x
s/[\\&]/\\&/g
s/^\.XX //
s/$/\//
x
s/^\\\.XX \(.*\)$/\/^\\.XX \/s\/\1//
G
s/\n//
(1) h:讲文本行放入存储空间。
(2) s/[][\\*.]/\\&/g:这个表达式难度比较大,如果在类表达,也就是"[]"中的第一个
字符是"]"的话,那么"]"就丧失了它的特殊含意;另外,唉"[]"中,仅仅只有"\"是有特
殊含意的,言下之意就是"*"、"."都是理解为字面意思,要使他们具有特殊意义就必须使
用"\"的转义了;虽然在表达式中没有出现,也要提一下,在"[]"中只有"^"出现在第一的
位置时,表示"非"的含意,其余情况就是字面解释,而"$"仅仅是在正则表达式的末尾时
才有特殊含意。"\\"去掉了"\"的特殊含意,"&"表示向前引用,因此,第二个命令的意思
就是:将模式空间中的"["、"] "、"\"、"*"、"."依次
用"\["、"\]"、"\\"、"\*"、"\."来替换。
(3) x:交换模式空间和存储空间。执行这个命令后模式空间的内容是原文的内容,而存
储空间中的内容发生变化,各个特殊字符都被替换成为了"\&"。
(4) s/[\\&]/\\&/g:对模式空间处理,出现的"\"或者"&"都会替换为"\\"或者"\&"。
(5) s/$/\//:这个好理解,就是在模式空间的结尾加上一个"/"。
(6) x:再次交换两个空间的内容。
(7) s/^\\\.XX \(.*\)$/\/^\\.XX \/s\/\1//:这个没有什么难度,就是那几个引用容易
把人看晕了,仔细一点,不会有问题的,就略过吧。
(8) G:略了。
(9) s/\n//:删除换行符。
这个脚本有什么用呢?用以下的文本实验就清楚了:
.XX "asterisk (*) metacharacter"
下面是每次命令的结果,第一行和第二行分别表示模式空间和存储空间的内容:
1. .XX "asterisk (*) metacharacter"
.XX "asterisk (*) metacharacter"
.XX "asterisk (*) metacharacter"
\.XX "asterisk (\*) metacharacter"
\.XX "asterisk (\*) metacharacter"
\.XX "asterisk (\*) metacharacter"
\.XX "asterisk (\*) metacharacter"
"asterisk (*) metacharacter"/
"asterisk (*) metacharacter"/
s命令的第二部分,"\"和"&"都是有特殊含意的,所以要预先转义掉其特殊含意。
明白了吗?当你希望用一个shell脚本自动生成一个主要是替换命令的sed脚本的时候,会
发现这个以上的内容对特殊字符的处理是多么得关键。
出了上面的应用,存储空间甚至还能够将很多行的内容存储起来供以后的输出。实际上,
这一功能对html等具有非常明显的结构的文本非常有效。下面是相关的例子:
例10
cat expl.10
<p>My wife won't let me buy a power saw. She is afraid of an
accident if I use one.
So I rely on a hand saw for a variety of weekend projects like
building shelves.
However, if I made my living as a carpenter, I would
have to use a power
saw. The speed and efficiency provided by power tools
would be essential to being productive.
sed and awk are power tools for editing.</p>
can be done interactively with a text editor. However,
using these programs can save many hours of repetitive
work in achieving the same result.</p>
> H
> d
> }
> /^$/{
> x
> s/^\n/<p>/
> s/$/<\/p>/
> G
> }' expl.10
运行一下这个命令,看看结果是怎样的。其实结果已经不重要了。通过这个子,应该学会
的是脚本中体现的流程控制的思想。脚本的第一部分使用"!"表示对不匹配的行进行处
理,但是这种处理因为"d"的存在,不会走脚本的底部,自然也就不会有任何的输出;在
脚本的第二部分中,脚本的确是到了最后的,相应的也清除了模式空间和存储空间的内
容,为读入下一段做好了准备。
果?显然,文本的最后一段不会被输出。这种情况怎么处理呢?最明智的办法就是自
己"制造"一个空行。新的脚本是这样的:
> /^$/!{
> H
> s/.*//
> }
> }
> /^$/!{
> H
> d
> }
> /^$/{
> x
> s/^\n/<p>/
> s/$/<\/p>/
> G
> }' expl.10
流程控制命令
为了使使用者在书写sed脚本的时候真正的"自由",sed还允许在脚本中用":"设置记号,
然后用"b"和"t"命令进行流程控制。顾名思义,"b"表示"branch","t"表示"test";前者
就是分支命令,后者则是测试命令。
冒号开始。冒号与变迁之间不允许有空格或者制表符,标签最后如果有空格的话,也会被
认为是标签的一部分。
[address]b[label]
它的含意是,如果满足address,则sed流程跟随标签跳转:如果标签指明的话,脚本首先
假设这个标签在b命令以下的某行,然后转入该行执行相应的命令;如果这个标签不存在
的话,控制流程就直接跳到脚本的末尾。否则继续执行后续的命令。
而b命令则给予使用者足够的自由在sed脚本中选择哪些命令应该被执行,哪些命令不应该
被执行。下面提供几种b命令的经典用法:
:top
command1
command2
/pattern/b top
command3
(2) 忽略某些不满足条件的命令:
command1
/patern/b end
command2
:end
command3
(3) 命令的两个部分只能执行其中一个:
command1
/pattern/b dothere
command
b
:dothere
command3
t命令的格式和b命令是一样的:
[address]t[label]
它表示的是如果满足address的话,sed脚本就会根据t命令指示的标签进行流程转移。而
标签的规则和上面讲的b命令的规则是一样的。下面也给出一个例子:
s/pattern/replacement/
t break
command
:break
还是用例6的sed脚本为例子。其实仔细思考一下就会发现这个脚本不是足够强大:如果某
个@fl结构跨越了两行,比如说三行怎么办?这就需要下面这个加强版的sed了:
$ cat expl.6.sed
:begin
/@fl(\([^)]*\))/{
s//\\fB\1\\fR/g
b begin
}
/@fl(.*/{
N
s/@f1(\([^)]*\n[^)]*\))/\\fB\1\\fR/g
t again
b begin
}
:again
P