definitive-guide-to-sed
Chapter 01: Introduction to sed
PatSpace and HoldSpace
- awk和perl,也能做'stream editing'的事情,也能起到和sed一样的作用.但是各有特点:
- awk在输出方面更有研究,而sed在输入输出方面都不错,也会有把sed的输入给awk让awk
更加优美的输出的事情
- perl其实是一门语言了,而且特别的难以理解,和sed这种小工具就不是一个维度的了.
- sed是有Lee McMahon在bell实验室发明的,其名字的来历是ed编辑器(Ken Thompson发明),
可能你会惊讶,ed这种编辑器每次只能编辑一行,是上古时代计算机计算性能低下导致的,
但是ed还是引入了很多新的强大的command,以及正则表达式,从而影响了如下振聋发聩
的名字:awk,ex,grep,sed, perl,vi
- sed拥有自己的scripting语言,但是这门语言不是我们通常理解的那种,它没有那么复杂,
比如它就没有变量和数组的概念
- 但是sed是拥有其两个特殊的概念:
- buffer: 我们叫做PatSpace
- workspace: 我们叫做HoldSpace
- PatSpace(pattern space)是sed主要的工作区域,大部分的sed其实就是读取并且更改
PatSpace: sed把input text一行接一行(line by line)的读取到patspace,每当读取完
一行以后,它就开始一次'Cycle',来处理读取到的这一行,这个Cycle通常是:
- sed保存着一个line counter从1开始,每当一行读取完毕,Cycle开始之前,都会增加这个
counter
- 对每一行,sed都会执行sed script给予的命令(这个script命令一般都很短,但是其实
很长也可以的,只不过很长的话,我们可能会单独写一段python或者perl了)
- 当sed script都指向完了以后,sed从通常会自动打印PatSpace里面的内容,清空
PatSpace,然后读取下一行到PatSpace,开始新的一轮Cycle
- 我们看到PatSpace就是为一次的Cycle准备的,下一次Cycle开始的时候,上一次的内容就
已经从PatSpace里面清空了,为了能够有更好的扩展性,sed引入了另外一个概念HoldSpace
这个space在Cycle之间是不clear的.
- 普通用户是用不到HotSpace的,高端用户可以用到HotSpace,但是HotSpace确实是有很多
令人费解的地方,需要小心.本书会介绍HotSpace的使用,即便你用不到,至少可以理解概念
Introducing the s Command
- s(substitute)是sed里面最最常用的命令,如果你只需要理解一个sed命令,那么这个命令
肯定是s
- s命令其实就是find and replace.假设我们的old.txt里面只有一个文字old,运行下面
的命令就可以把old转换成new,并打印到standard output(注意old.txt文件不变)
hfeng@ sed $ cat old.txt
old
hfeng@ sed $ sed 's/old/new/' old.txt
new
hfeng@ sed $ cat old.txt
old
- 从PatSpace的角度我们来分析一下这个过程:
- sed把'old'读取到PatSpace
- Trailing newline被移走
- s命令运行,把'old'转化成'new'
- sed把trailing newline加回来
- sed 'AutoPrints(print and clear)' PatSpace('new')
- 没有更多的input了, sed退出
- s命令真正的script syntax如下
's/RegEx/SubEx/'
- 我们很自然的注意到s命令find的肯定不仅仅是exact match的字符串,同样要包括正则
表达式,这个范围就广泛的多了
- s命令replace的也可以不仅仅是exact match的字符,SubEx代表的是'substitution
expression',这是一种比较灵活的字符串,后面会介绍
- 如果我们的RegEx没有被sed匹配到,那么s命令什么也不会做,输入到PatSpace里面的字符
会原封不到的打印出去
hfeng@ sed $ cat old.txt
old
hfeng@ sed $ sed 's/red/blue/' old.txt
old
Quoting Command-Line Scripts
- 在上面的例子中's/old/new'就是我们的sed script,这个script有多重的quote方法:
Chapter 02: sed s Command(substitue)
Delimiter for s Command
- 前面说到过这段script 's/RegEx/SubEx/', 这里面的'/'就是分隔符(delimiter),对于
分隔符有一个偏见就是一定要使用'/',其实不是这样子的,任何可见的char都可以作为分
隔符,只不过'/'是常用手段罢了
hfeng@ sed $ echo old | sed 's/old/new/'
new
hfeng@ sed $ echo old | sed 's:old:new:'
new
hfeng@ sed $ echo old | sed 's|old|new|'
new
hfeng@ sed $ echo old | sed 's_old_new_'
new
- 既然什么char都可以作为分隔符,那万一我们的input stream里面就包含了分隔符怎么办
呢?办法自然是使用escape字符串
hfeng@ sed $ echo /A/ | sed 's/\/A\//\/B\//'
/B/
- 这个代码看着就晕,所以我们可以使用另外的字符作为分隔符
hfeng@ sed $ echo /A/ | sed 's:/A/:/B/:'
/B/
- 最后,需要记住的是分隔符永远是三个,缺少最后一个是常见错误
hfeng@ sed $ echo /A/ | sed 's:/A/:/B/'
sed: 1: "s:/A/:/B/": unterminated substitute in regular expression
sed Input from File or stdin
- sed经常和file来配合,一个配合的例子如下,文件名是放在最后面的.
hfeng@ sed (master) $ cat rgb
low(#1): "red green blue"
UPPER(#2): "RED GREEN BLUE"
hfeng@ sed (master) $ sed 's/RED/xxx/' rgb
low(#1): "red green blue"
UPPER(#2): "xxx GREEN BLUE"
- sed当然也可以从stdin里面读取,stdin的来源很多:
- 从keyboard:竟然可以从keyboard!
sed s/old/new/
old2 # input by hand
new2
# ctrl + D return
- 通过"|"从另外的程序获取stdin
$ echo old | sed 's/old/new/'
new
- 我们还可以使用'<'来强制从某个文件获取stdin,但是对于sed意义不大
$ sed 's/RED/xxx/' < rgb
low(#1): "red green blue"
UPPER(#2): "xxx GREEN BLUE"
sed Command Line Options
- Unix命令都需要command line options,sed也不例外,sed有12个option,其中5个非常常
用.后面会一一介绍:
- -e: add scrip tot end of over sed script
- -f: add lines in script-file to end of overall script)
- -i: edit input file in place
- -n: suppress AutoPrint of PatSpace
- -r: use extended regular expression
-e sed Command Line Option
- sed script是sed用来"对付"input lines的,我们可以使用-e来指定sed script:
- 指定一次的情况下,和不使用-e区别不大
hfeng@ sed (master) $ echo old | sed 's/old/new/'
new
hfeng@ sed (master) $ echo old | sed -e 's/old/new/'
new
- 指定多于一次的情况下,-e用处就大了.比如你想把'o'换成'=', 把'U'换成'-',那么你
可以两次使用-e,append两段sed script
hfeng@ sed (master) $ cat rgb
low(#1): "red green blue"
UPPER(#2): "RED GREEN BLUE"
hfeng@ sed (master) $ sed -e s/o/=/ -e s/U/-/ rgb
l=w(#1): "red green blue"
-PPER(#2): "RED GREEN BLUE"
- 现代的sed可以使用';'来append sed script,但是一般只能用在s命令上面,其他命令
会发生误会
$ sed 's/o/=/; s/U/-/' rgb
l=w(#1): "red green blue"
-PPER(#2): "RED GREEN BLUE"
-f sed Command Line Option
- sed script当然可以放到另外一个文件里面,这个时候使用-f来指定script file文件的
位置,这样script file里面可以使用回车分割多种的规则
hfeng@ sed (master) $ cat s1.sed
s/red/333/
s/GREEN/55555/
hfeng@ sed (master) $ cat rgb
low(#1): "red green blue"
UPPER(#2): "RED GREEN BLUE"
hfeng@ sed (master) $ sed -f s1.sed rgb
low(#1): "333 green blue"
UPPER(#2): "RED 55555 BLUE"
- script file里面不要谁用任何的引号,否则会出错
sed Output to File or stdout
- 如果想把sed的结果写入到其他文件,使用'>'就好了
hfeng@ sed (master) $ sed 's/old/new/' old.txt > temp
hfeng@ sed (master) $ cat temp
new
- 我们的sed可以和for循环配合,更改一系列文件里面的内容,复杂的sed script还可以写
在另外的文件里面
list='one.txt two.txt'
for file in $list; do
sed -f rename.sed $file > temp.x
mv temp.x $file
done
-i sed Command Line Option
- 前面我们说过,sed的最后跟的是一个文件,这个文件再sed过后不会改变.如果我们想改变
这个文件的内容,需要重定向,和另外一个中间文件
$ sed 's/A/B/' in.txt > temp
$ mv temp in.txt
- 这样做一来是比较麻烦,二来呢容易受到权限问题的困扰,所以我们使用一个option -i
来做到"最终把结果写回到文件"的作用.注意,在mac上,这个参数可能使用范围有限
test@openstest:~/tmp$ cat demo.txt
old
test@openstest:~/tmp$ sed -i 's/old/new/' demo.txt
test@openstest:~/tmp$ cat demo.txt
new
- 我们前面使用了一段script来把一系列文件里面的内容更改(还使用了for循环),如果使
用-i参数的话,就可以一行解决问题了
test@openstest:~/tmp/sed-playground$ cat one.txt
HELLO world
test@openstest:~/tmp/sed-playground$ cat two.txt
world HELLO
test@openstest:~/tmp/sed-playground$ sed -i 's/world/WORLD/' one.txt two.txt
test@openstest:~/tmp/sed-playground$ cat one.txt
HELLO WORLD
test@openstest:~/tmp/sed-playground$ cat two.txt
WORLD HELLO
Chapter 03: Flags for s(substitute) Command
- 我们来看一个简单的例子,更改old为new
test@openstest:~/sed-playground$ echo old old | sed s/old/new/
new old
- 我们发现,只有第一个old被更改了,这是因为如果不加特殊说明,s只更改第一次出现的匹
配,如果需要全部匹配,那么就需要给s命令增加g flag
$ echo old old | sed s/old/new/g
new new
- 我们的s命令的syntax就更新为
s/RegEx/SubEx/[flags]
- 我们下面来看看几个常见的flag
i(ignore case) Flag
- 如果不加flag的话,我们的匹配是大小写区分的,old不能匹配Old,因为RegEx就是区分
大小写的
~/github/sed-playground $ echo old | sed s/Old/xxx/
old
- 我们可以在s命令的flag里面加上ignore大小写
~/github/sed-playground $ echo old | sed s/Old/xxx/i
xxx
- 需要说明的是,除了i flag,我们还可以在正则表达式那里设置大小写不敏感
~/github/sed-playground $ echo ab | sed s/AB/=/i
=
~/github/sed-playground $ echo ab | sed s/[aA][bB]/=/i
=
g(global) and n(number) Flags
- g和n是两个关于替换的属性:
- 设置g表示全部替换
~/github/sed-playground $ echo oldold | sed s/old/new/
newold
~/github/sed-playground $ echo oldold | sed s/old/new/g
newnew
- 设置n表示替换第几次匹配
~/github/sed-playground $ echo oldold | sed s/old/new/
newold
~/github/sed-playground $ echo oldold | sed s/old/new/2
oldnew
- 对于不设置n来说,就是默认为1,而且如果n很大的话(没匹配到),那么什么都不会发生
~/github/sed-playground $ echo oldold | sed s/old/new/9
oldold
p(print) and w(write) Flags
- 我们前面讲过sed的AutoPrint,在s命令的flag里面,我们还可以加上一个s命令的打印
要求'p',这个打印和AutoPrint是两个完全不同的命令,它比AutoPrints要求要严格
一点,它要求一定要匹配了,才打印.所以如果我们使用p,又恰巧匹配的话,会有双重效果
~/github/sed-playground $ echo ABC | sed s/ABC/xxx/p
xxx
xxx
- 为了让这个命令的语义更纯粹,我们通常把这个命令和sed的一个command line option
一起使用(注意,是command line option,而不是flag),这个option就是n,它的作用是
关掉AutoPrint,这样的话flag p的作用就会很明显:能够匹配的才打印,否则就不打印
~/github/sed-playground $ cat rgb
low(#1): "red green blue"
UPPER(#2): "RED GREEN BLUE"
~/github/sed-playground $ sed -n 's/RED/xxx/p' rgb
UPPER(#2): "xxx GREEN BLUE"
- 和p相似的一个flag是w,它也是在replace发生的时候才起作用!起的作用是把结果写
到一个文件里面
~/github/sed-playground $ cat rgb
low(#1): "red green blue"
UPPER(#2): "RED GREEN BLUE"
~/github/sed-playground $ cat a.txt
=======had some text========
~/github/sed-playground $ sed 's/red/xxx/w a.txt' rgb
low(#1): "xxx green blue"
UPPER(#2): "RED GREEN BLUE"
~/github/sed-playground $ cat a.txt
low(#1): "xxx green blue"
- 上面的例子有以下几点需要说明:
- 只有真正replace的才会写入到文件
- 文件原来存在的话,内容会被clear
- sed原来的内容不会影响到w写入,sed的结果会显示带stdout,真正replace的结果会
写入到文件里面
e(execute) and m(multi) Flags
- 这个flag很有bash特色,是把匹配成功的字符串作为一个命令来运行,注意,一定是匹配
成功的字符串
test@openstest:~/sed-playground$ expr 2 + 4
6
test@openstest:~/sed-playground$ echo 2 | sed 's/./expr & + 4 /e'
6
- multi-line command flag会更常用一点:这个flag通常会和'N;'一起起作用,所以我们
要先看看'N;',这个命令会把多行通过'\n'联系起来
$ seq 2 | sed 'N;s/1\n2/=/'
=
- 这种情况下的最大问题,是我们原来用来'区别'不同行的Anchor MetaChars信息,在'N;'
的处理下,全部消失了
$ seq 2 | sed 'N;s/^2/=/'
1
2
- 我们使用multi-line mode+'N;'的话,等于在'1$'和'^2'之间加了'\n'
$ seq 2 | sed 'N;s/^2/=/m'
1
=
Combining s Command Flags
- 所有s命令的flag都可以结合起来使用,但是有一些规则需要规避:
- w flag如果有,必须在最后,后面跟文件名(最好加一个空格)
- 不要重复使用flag
- n和g两个flag结合起来就是从n开始替换n, n+1, n+2等等
- 例子如下:
- 大小写不敏感的全部替换
~/github/sed-playground $ sed 's/r/+/ig' rgb
low(#1): "+ed g+een blue"
UPPE+(#2): "+ED G+EEN BLUE"
- 全部替换,但成功的才打印
~/github/sed-playground $ cat rgb
low(#1): "red green blue"
UPPER(#2): "RED GREEN BLUE"
~/github/sed-playground $ sed -n 's/e/+/gp' rgb
low(#1): "r+d gr++n blu+"
- 更改从第3次match开始的元音
~/github/sed-playground $ sed 's/[aeiou]/+/3g' rgb
low(#1): "red gr++n bl++"
UPPER(#2): "RED GREEN BLUE"
Chapter 04: Single Character MetaChars
- s命令的第一个参数是RegEx,也就是一个regular expression,正则表达式一个字母所能
表示的范围非常的广,可能是一个char,也可能是一系列的char(比如[A-Z])
Literal Character in RegEx
.(Wildcard Character)
- literal总体上来说,是比较守规矩的,一个对一个.另外一种正则的成员MetaChar
(metacharacter)就没那么简单了
- 最常见的MetaChar是'.',它能够对应任何单个字符(any single character)甚至是换
行符.我们也可以理解为'.'可以对应任何的literal
- 使用'.'要非常小心,因为它会极大的提高你要对应的字符串数量:
- 比如hat可以对应hat, hatch, shatter
- h.t就可以匹配hat,hit,hot,hatch等等更多的"合理的"字符串
- h.t还可以匹配h5t,h=t,hjt等可能你并不需要的字符串
- 一个使用'.'的例子
~/github/sed-playground $ cat rgb
low(#1): "red green blue"
UPPER(#2): "RED GREEN BLUE"
~/github/sed-playground $ sed 's/.E/++/g' rgb
low(#1): "red green blue"
UP++R(#2): "++D G++EN BL++"
\ (Specify Literal Character)
- '.'本身其实也是一个literal,但是如果不加任何修饰的话,就成了MetaChar,如果我们
需要'.'的literal的话,需要使用escape char '\'
~/github/sed-playground $ echo 103 | sed 's/1.3/1.4/'
1.4
~/github/sed-playground $ echo 103 | sed 's/1\.3/1.4/'
103
- 所有的MetaChar想使用自己的literal的时候都要用到escape char
MetaChar |
Escaped literal |
* |
\* |
^ |
\^ |
$ |
\$ |
[ |
\[ |
. |
\. |
\ |
\\ |
[] (Character Set)
- 单个的literal的能力太局限,MetaChar的能力又太广泛,能力在这两者之间的就是
character set:不至于只能匹配一个,但是也不至于所有都匹配,可以在一个[]里面限
定好自己需要的数量,比如[aeiou]就可以用来只匹配原因
~/github/sed-playground $ echo abet | sed s/[aeiou]/=/g
=b=t
- character set最朴素的写法当然是所有的罗列,比如[aeiou],,但是也
可以使用'-'来作为through的意思:
- [0-9]所有的数字
- [a-z]所有的小写字母
- [A-M], A到M之间所有的大写字母
- 'x-y'可以相互的join起来,变成更强大range的character set
- [a-zA-Z] alphabetic
- [a-zA-Z_] alphabetic or _
- [a-zA-Z0-9] alphanumeric
- [a-zA-Z0-9_] alphanumeric or _
- 如果希望把']'也作为character set的一部分的话,它必须在最左边(不能再右边,以
防被误判成右边的character set边界)
~/github/sed-playground $ echo '()[]{}' | sed 's/[])}]/+/g'
(+[+{+
- 如果希望'-'也作为character set的一部分的话,它必须在最左边或最右边(不能在中
间,以防止被误判成range)
- '^'的用法很特别,总体上来说,'^'必须在character set的最前面,表示对它后面的
character set进行取反,但是实际用处有两种:
- 整个正则表达式只有character set,那么就是'取反某些character set的作用'
~/github/sed-playground $ echo '()[]{}' | sed 's/[^])}]/+/g'
+)+]+}
- 整个character set是其他的正则表示式的一部分的情况下,就是用来缩小匹配区间
的(因为匹配是贪婪的),比如下面的例子,'e.*e'的匹配如果中间不加上[^e]的话,就
会从"第一个e开始,匹配到最后一个e",而我们希望从"第一个e开始,匹配到第二个e"
~/github/sed-playground $ cat rgb
lower(#1): "red green blue"
UPPER(#2): "RED GREEN BLUE"
~/github/sed-playground $ sed 's/e.*e/++++/' rgb
low++++"
UPPER(#2): "RED GREEN BLUE"
~/github/sed-playground $ sed 's/e[^e]*e/++++/' rgb
low++++d green blue"
UPPER(#2): "RED GREEN BLUE"
\w \W (Word and Non-Word)
- \w和\W看起来像是literal,其实是character set:
- \w代表所有的'Word',等于[a-zA-Z0-9_]
- \W代表所有的非'Word',等于[^a-zA-Z0-9_]
- 这里又有一个新的概念叫做sed word,如果一个空格分割的字符串,其每个成员都是
[a-zA-Z0-9_],那么它就是sed word.否则不是
- bit_flag就是sed word
- bit-flag就不是sed word
- 看两个例子:
- 're'能够匹配'r\w',注意这里分隔符使用的是'!'
~/github/sed-playground $ cat rgb
lower(#1): "red green blue"
UPPER(#2): "RED GREEN BLUE"
~/github/sed-playground $ sed 's!r\w!++!' rgb
lower(#1): "++d green blue"
UPPER(#2): "RED GREEN BLUE"
- 'ue"'能够匹配'u\w\W'
~/github/sed-playground $ cat rgb
lower(#1): "red green blue"
UPPER(#2): "RED GREEN BLUE"
~/github/sed-playground $ sed 's!u\w\W!+++!' rgb
lower(#1): "red green bl+++
UPPER(#2): "RED GREEN BLUE"
[::] (Posix Character Class)
- character set还有一种更加"语义化"的写法,就是使用一段英文来替代[:xxx:]里面
的xxx,用来代表某一个character set.注意,posix character class需要两层的中括
号,[:black:]等同于\t,所以:black:等同于[\t]
- 这种做法更加的容易理解,并且有时候会在不同的locale里面引入其他字符串,比如在
西班牙语的locale里面[:alpha:]就等于aeiou加上它们认为是元音的其他字符(比如v)
- 下面是常见的posix character和character set的对应
Posix Character |
Character Set |
[:alnum:] |
a-zA-Z |
[:blank:] |
\t |
[:digit:] |
0-9 |
[:lower:] |
a-z |
[:space:] |
\t\r\n\v\f |
[:upper:] |
A-Z |
[:xdigit:] |
A-Fa-f0-9 |
- posix 也是可以和其他character set组合的:
- [[:digit:]ab]等同于[0-9ab]
- [[:lower:]12]等同于[a-z12]
Chapter 05: Anchor MetaChars
- 所谓的'Anchor MetaChar'是这样一种char,它们既不占用空间,也不匹配任何字符串,
它起到的是"边界"判断的作用,比如判断是不是在PatSpace的开头
^(Start of PatSpace)
- 在vi里面我们也见到了,^是表示一行的开头.
- 首先^能够自己起作用,这个时候因为^不占用空间,所以sed的replace会转化为insert
~/github/org/notes/misc $ echo XYZ | sed 's/^/=/g'
=XYZ
- ^和其他literal或者Metachar配合就能有更准确的匹配,insert也就会回归成replace
了
~/github/org/notes/misc $ echo XYZ | sed 's/^X/=/g'
=YZ
- 如果^不在最前面,那么它就是自己literal的作用
~/github/org/notes/misc $ echo 2^3=8 | sed 's/2^3/8/'
8=8
- 如果第一个字符恰好是^,那么使用^^来匹配
~/github/org/notes/misc $ echo ^HDR | sed 's/^^HDR/=/'
=
$(End of PatSpace)
- ^的counterpart就是$,$表示PatSpace的最后,除了表示的位置相反,其所有的用法和^
是一样的
- '^$'联合起来可以表示空行
\< \> \b (Word Boundaries)
\B (Not a Word Boundary)
- \B就是\b的反面,匹配任何不是word boundar的位置
~/github/org/notes/misc $ echo 'cow scow' | sed 's/\Bcow/++/'
cow s++
Chapter 06: Simple Repetition MetaChars
- Repetition MetaChar顾名思义,就是能够确认其前面的char(或者 character set)能够
重复多少次的字符
\+ (1 or More of Previous)
- 相比于*, '\+'会清晰的多,因为它会至少要求有一个,就避免了0,这个灰色地带,比如
'b\+'就代表一个或者多个b
test@openstest:~/sed-playground$ echo abc | sed 's/b\+/=/'
a=c
test@openstest:~/sed-playground$ echo abbbc | sed 's/b\+/=/'
a=c
-r sed Command Line Option
- '\+'的问题在于需要写一个'\',这是为了和真正的'+'进行区别.但是真实的情况下'+'
出现的概率并不高,所以正则表达式发明了一种叫做regexp extend的情景,在这种情景
下,你可以使用'+'来替代'\+'
- 在sed里面,是使用-r(–regexp-extended)来代表我们的正则表达式是"扩展的":
test@openstest:~/sed-playground$ echo abbv | sed 's/b\+/=/'
a=v
test@openstest:~/sed-playground$ echo abbv | sed 's/b+/=/'
abbv
test@openstest:~/sed-playground$ echo abbv | sed -r 's/b+/=/'
a=v
- 那么我们在"扩展的"正则表达式里面想匹配'+'怎么办呢?答案是使用'\+',是不是很有
哲学感:"扩展的"正则表达式设计的初衷就是,'+'出现的概率非常低,而'一次或者多次'
的使用概率高很多.我们让'一次或者多次'的使用更加便捷
$ echo abb+ | sed -r 's/b\+/=/'
ab=
- 从上面的例子我可以看出,扩展的正则表达式其实完全等价于普通正则表达式,只是基
于"概率"来做了一些改良,所以,如果可能的话,我们尽量要使用"扩展的"正则表达式
- 需要注意的是,我们的"扩展的"正则表达式并不是要取消'\'的使用,而是要减少'\'的
使用,有些情况下也是不可避免的要使用'\':
- 前面说了,匹配'+'的时候,就必须要要使用'\+'了
- boundary metachar的'\'不可省略:'\<', '\>', '\b', '\B'
\? (0 or 1 Previous)
- \? 是一种"存在与否"的判断,要么有一个,要么没有.所以它还是和'*'一样,涉及到了
0这个灰色地带要非常小心(后面,我们都会使用扩展的正则)
$ echo abc | sed -r 's/x?/=/g'
=a=b=c=
- 常见的用法是匹配'HA',但是前后可以有'T'
test@openstest:~/sed-playground$ echo THAT | sed -r 's/T?HAT?/++/'
++
test@openstest:~/sed-playground$ echo HA | sed -r 's/T?HAT?/++/'
++
Compare * \+ \?
- 我们再来总结下如下三个重要的repetition metachars
- '*': 0 or more of previous
- '\+': 1 or more of previous
- '\?': 0 or 1 of previous
- 匹配'zero occurrence'看起来有些滑稽,有些人认为'\+'(1 or more)会比'*'(0 or more)
更加的常用,但是其实是相反的,'*'的使用频率会多一点
- 对于'*'和'\?',因为会涉及到nothing,所以要特别的小心,因为他们总会匹配到开头,
在不设置g flag的时候,往往只会更改PatSpace的开头. '\+'就不会有这个烦恼
test@openstest:~/sed-playground$ echo abbc | sed 's/b*/=/'
=abbc
test@openstest:~/sed-playground$ echo abbc | sed 's/b\?/=/'
=abbc
test@openstest:~/sed-playground$ echo abbc | sed 's/b\+/=/'
a=c
Chapter 07: General Repetion MetaChars
- 前面介绍了一些简单的repetion metachar,我们这里介绍一下更general的设置,这种
general的设置是可以完全覆盖前面的simple设置的
\{N\} (Exact N of Previous)
- 顾名思义,就是可以精确匹配多次出现,比如'x\{3\}'就是意味着'xxx'
- 当然了,你其实是可以手动写数目的,麻烦一点而已.如果N是100的话,想你也不会不用这个设置
test@openstest:~/sed-playground$ echo abbbbbbbbbbc | sed -r 's/b{10}/=/'
a=c
test@openstest:~/sed-playground$ echo abbbbbbbbbbc | sed -r 's/bbbbbbbbbb/=/'
a=c
\{L,\} (Low, Higher of Previous)
- '\{L, }'就是通常意义上的"至少有L个重复"
test@openstest:~/sed-playground$ echo abc | sed -r 's/b{2,}/#/'
abc
test@openstest:~/sed-playground$ echo abbc | sed -r 's/b{2,}/#/'
a#c
test@openstest:~/sed-playground$ echo abbbc | sed -r 's/b{2,}/#/'
a#c
- 所以'*'其实就是'\{0, \}'的simple写法
- '\+'是'\{1,\}'的simple写法
\{L, H\} (Low, High of Previous)
- 这个也很"顾名思义"了,看一个例子'b\{2,3\}'匹配bb或者bbb
test@openstest:~/sed-playground$ echo abc | sed 's!b\{2,3\}!=!'
abc
test@openstest:~/sed-playground$ echo abbc | sed 's!b\{2,3\}!=!'
a=c
test@openstest:~/sed-playground$ echo abbbc | sed 's!b\{2,3\}!=!'
a=c
test@openstest:~/sed-playground$ echo abbbbc | sed 's!b\{2,3\}!=!'
a=bc
- '\?'其实也就是'\{0, 1\}'
Chapter 08: Other RegEx MetaChars
\| (Alternative Patterns)
- '\|' 允许我们的regexp有多个候选人,比如我们想把red或者BLUE都替换成xxx,那么就
可以如下
test@openstest:~/sed-playground$ sed "s/red\|BLUE/xxx/g" rgb
low(#1): "xxx green blue"
UPPER(#2): "RED GREEN xxx"
test@openstest:~/sed-playground$ sed -r "s/red|BLUE/xxx/g" rgb
low(#1): "xxx green blue"
UPPER(#2): "RED GREEN xxx"
\( \) (Grouping and Saving)
- \(\)可以定义一个'group',定义的这个group可以在后面被重复使用(通过\1 \2),因为
涉及到了position,这个的使用有一些巧妙效果,比如reverse两个char的字符串
$ echo xy | sed -r 's/(.)(.)/\2\1/'
yx
- \(\)还可以跟repetition metachar一起使用,比如去除偶数个的'A'
test@openstest:~/sed-playground$ echo AA | sed -r 's/(AA)+/=/'
=
test@openstest:~/sed-playground$ echo AAA | sed -r 's/(AA)+/=/'
=A
test@openstest:~/sed-playground$ echo AAAA | sed -r 's/(AA)+/=/'
=
\`(Always Start of PatSpace)
- 高端特性,强制版本的'^'.
- 为什么说是强制版本的'^'呢,因为'^'的意义是会改变的:
- 在普通模式下,'^'会去匹配PatSpace里面最开始的empty string
# 在普通模式下, ^功能单一
test@openstest:~/sed-playground$ seq 2 | sed 's/^2/=/'
1
=
test@openstest:~/sed-playground$ seq 2 | sed 'N;s/^2/=/'
1
2
- 在multi-line模式下面, '^'变成了匹配newline+empty string
test@openstest:~/sed-playground$ seq 2 | sed 'N;s/^2/=/'
1
2
test@openstest:~/sed-playground$ seq 2 | sed 'N;s/^2/=/m'
1
=
- '\`'就永远不改变自己的节操,永远匹配最开始的empty string
test@openstest:~/sed-playground$ seq 2 | sed 'N;s/\`2/=/'
1
2
test@openstest:~/sed-playground$ seq 2 | sed 'N;s/\`2/=/m'
1
2
\'(Always End of PatSpace)
- 高端特性,强制版本的'$',非常不常用,主要配合multi-line mode
Chapter 09: SubEx MetaChars
- SubEx = 'Substitution Expression'. SubEx指的是被替代的部分,也就是s命令的第二
个'//'里面的内容
& (Entire Matched Portion)
- 在第一个'//'里面成功匹配的部分,会被保存在'&'里面,如果在第二个'//'里面有&的
话,就等于我们要把匹配结果都再显示出来
$ seq 3 | sed 's/./Line &/'
Line 1
Line 2
Line 3
- 当然了,如果你像使用literal &的话,需要在前面加一个'\'
test@openstest:~/sed-playground$ sed 's/...../[&]/' rgb
[lower](#1): "red green blue"
[UPPER](#2): "RED GREEN BLUE"
test@openstest:~/sed-playground$ sed 's/...../[\&]/' rgb
[&](#1): "red green blue"
[&](#2): "RED GREEN BLUE"
\N BackRef(Play Saved Group)
- 每次把整个匹配的结果都打印出来,在很多时候并没有意义,很多时候,我们只希望使用
匹配结果的一部分.sed为了能够实现这个需求,从匹配的部分入手,让我们的匹配成功
的部分分成了好几个group:使用()来区分
- 然后我们在SubEx里面,就是使用'\N'来打印第N次匹配成功的结果啦,最经典的例子就是
reverse xy
$ echo xy | sed -r 's/(.)(.)/\2\1/'
yx
- 当然不仅仅是reverse,很多时候,我们只需要一部分的匹配结果,比如第一部分
$ echo xy | sed -r 's/(.)(.)/a\1/'
ax
- 需要注意的是BackRef也可以是RegEx的一部分!
$ echo xx | sed -r 's/(.)\1/\1/'
x
\l \u (Case for Next Character)
- '\l'把下一个char变成小写,'\u'char变成大写,看例子
test@openstest:~/sed-playground$ sed 's/./\u&/g' rgb
LOWER(#1): "RED GREEN BLUE"
UPPER(#2): "RED GREEN BLUE"
test@openstest:~/sed-playground$ sed 's/./\l&/g' rgb
lower(#1): "red green blue"
upper(#2): "red green blue"
\L \U \E (Case for Next Span)
- '\L' 把剩余的SubEx变成lowercase,知道遇到'\E'(或者'\U')
- '\U' 把剩余的SubEx变成uppercase,知道遇到'\E'(或者'\L')
Chapter 10: Command Addresses
- 每个sed comamand在其前面有个可选的参数,叫做Address.Address要紧贴着在command
之前
- 如果你不设置Address,那么command"一直"会运行,但是你设置了Address,那么command
就会有选择的运行啦.
- 为了让我们的例子更加生动,我们先采取下11章才会介绍的delete命令,如下删除能够匹
配的那一整行(PatSpace)
test@openstest:~/sed-playground$ cat rgb
lower(#1): "red green blue"
UPPER(#2): "RED GREEN BLUE"
test@openstest:~/sed-playground$ sed '/\<GREEN\>/d' rgb
lower(#1): "red green blue"
Address Omitted
- 先看一个没有address的例子,command总是运行
N Format Address
- 在看一个只有一个integer作为Address的例子:只删除第一个
- '$'是比较特殊的一个N,它可用来表示让command运行于最后一个input
L,H Format Address
- 这种address就是设置一个运行command的区间
- 一般来说L要小于H,如果不是的话,只有L会执行
$ seq 4 | sed '3,1d'
1
2
4
/RegEx/ Format Address
- A '/RegEx/'作为address就是说,如果能够match到这个reg,那么我们就指向command
test@openstest:~/sed-playground$ cat rgb
lower(#1): "red green blue"
UPPER(#2): "RED GREEN BLUE"
test@openstest:~/sed-playground$ sed '/red/ d' rgb
UPPER(#2): "RED GREEN BLUE"
/RegEx/,/RegEx/ Address
- 就是reg也可以做个range
test@openstest:~/sed-playground$ cat a-i.txt
a
b
c
d
e
f
g
h
i
test@openstest:~/sed-playground$ sed '/a/,/g/ d' a-i.txt
h
i
L,/RegEx/ Format Address
- 就是从L行开始,遇到/RegEx/就结束
test@openstest:~/sed-playground$ cat a-i.txt
a
b
c
d
e
f
g
h
i
test@openstest:~/sed-playground$ sed '1, /e/ d' a-i.txt
f
g
h
i
/RegEx/,+N Format Address
- 就是遇到/RegEx/开始,再运行N次结束(注意这个再)
test@openstest:~/sed-playground$ cat a-i.txt
a
b
c
d
e
f
g
h
i
test@openstest:~/sed-playground$ sed '/b/, +2 d' a-i.txt
a
e
f
g
h
i
/RegEx/,~N Format Address
- 遇到RegEx开始,到第Nth行结束
test@openstest:~/sed-playground$ cat a-i.txt
a
b
c
d
e
f
g
h
i
test@openstest:~/sed-playground$ sed '/b/,~3 d' a-i.txt
a
d
e
f
g
h
i
First~Step Address
- 就是从第First行开始运行,然后First+Step运行,First+Step+Step运行等等
! (Inverts Address Match)
Chapter 11: Delete PatSpace Cntent - dD
sed d Command(delete)
- delete是和substitute地位一样的删除命令,删除的是真个PatSpace
- 一般来说,只使用d命令是没有意义的,只会全部删除
- 所以d命令一般都是和其他前面的Address紧密联系,上一章我们已经看到了
sed D Command
- 如果没有'\n'的话,那么D和d命令是一致的,但是因为引入了'\n'的话.就没有restart
一说了,这个时候需要一个新的命令,那就是D
- D不是删除这个PatSpace,而删除第一行(#1)外加一个newline
Chapter 12: Append, Insert, Change - aic
sed a Command(append)
- a命令是往PatSpace的打印结果里面append,不是向PatSpace里面append
- 往结果最后加字符串
test@openstest:~/sed-playground$ cat rgb
lower(#1): "red green blue"
UPPER(#2): "RED GREEN BLUE"
test@openstest:~/sed-playground$ sed '$ a zzz' rgb
lower(#1): "red green blue"
UPPER(#2): "RED GREEN BLUE"
zzz
- 匹配到red以后,在下一行加文字
test@openstest:~/sed-playground$ cat rgb
lower(#1): "red green blue"
UPPER(#2): "RED GREEN BLUE"
test@openstest:~/sed-playground$ sed '/red/ a zzz' rgb
lower(#1): "red green blue"
zzz
UPPER(#2): "RED GREEN BLUE"
sed i Command(insert)
- 和a对应,a是匹配到了,"下一行"加文字,i命令是匹配到了"上一行"加文字
test@openstest:~/sed-playground$ cat rgb
lower(#1): "red green blue"
UPPER(#2): "RED GREEN BLUE"
test@openstest:~/sed-playground$ sed '/red/ i zzz' rgb
zzz
lower(#1): "red green blue"
UPPER(#2): "RED GREEN BLUE"
sed c Command(change)
- change顾名思义,就是更改啦
test@openstest:~/sed-playground$ cat rgb
lower(#1): "red green blue"
UPPER(#2): "RED GREEN BLUE"
test@openstest:~/sed-playground$ sed '1 c xxx' rgb
xxx
UPPER(#2): "RED GREEN BLUE"
Chapter 13: Print PatSpace -pPl
sed p Command(print)
- p命令是用来打印PatSpace的,我们知道PatSpace是有AutoPrint的,但是是在sed script
完毕的时候才会调用.如果我们想在其他时间打印PatSpace的情况,那么p命令是不二之选
- 比如我们在替换之前先看我们要替换的是sha
$ cat rgb | sed 'p; s/red/xxx/'
lower(#1): "red green blue"
lower(#1): "xxx green blue"
UPPER(#2): "RED GREEN BLUE"
UPPER(#2): "RED GREEN BLUE"
- p命令当然可以选择打印其中某一行,一般这种情况下都要调用sed command option的-n
停止AutoPrint
test@openstest:~/sed-playground$ sed '/2/ p' rgb
lower(#1): "red green blue"
UPPER(#2): "RED GREEN BLUE"
UPPER(#2): "RED GREEN BLUE"
test@openstest:~/sed-playground$ sed -n '/2/ p' rgb
UPPER(#2): "RED GREEN BLUE"
sed P Command(Print)
- 在没有'\n'的情况下P和p命令完全相等
- 在有'\n'的情况下,P命令会打印#1直到第一个'\n'
sed l Command(display line)
- 对于'\n'困扰多时的人来说,多么希望能够明确的知道当前的PatSpace里面有没有'\n'
这个时候l命令出现了,它能够打印出'\n',在debug的时候非常有用
test@openstest:~/sed-playground$ seq 3 | sed -n 'l'
1$
2$
3$
test@openstest:~/sed-playground$ seq 3 | sed -n 'N;l'
1\n2$
Chapter 14: Read/Write File - rR wW
sed r Command(read RFile)
- 就是在Address配置的位置,进行读取文件的操作
test@openstest:~/sed-playground$ cat rgb
lower(#1): "red green blue"
UPPER(#2): "RED GREEN BLUE"
test@openstest:~/sed-playground$ seq 2 | sed '1 r rgb'
1
lower(#1): "red green blue"
UPPER(#2): "RED GREEN BLUE"
2
sed R Command (Read Rfile)
sed w Command (write Wfile)
- 把结果写入到文件,注意,只有Address部分会写入文件,AutoPrint只会打印到stdout
test@openstest:~/sed-playground$ seq 2 | sed '1 w a.txt'
1
2
test@openstest:~/sed-playground$ cat a.txt
1
test@openstest:~/sed-playground$ seq 2 | sed '2 w a.txt'
1
2
test@openstest:~/sed-playground$ cat a.txt
2
sed W Command(Write Wfile)
Chapter 15: Read Line into PatSpace -nN
sed n Command (next line)
- n命令就是把next line读取到PatSpace
sed N Command (Next Line)
- 相比于n命令,N命令更加重要,它在next line读取到PatSpace之前,先加了一个newline
不过这个newline得靠l读取出来
test@openstest:~/sed-playground$ seq 3 | sed -n 'l'
1$
2$
3$
test@openstest:~/sed-playground$ seq 3 | sed -n 'N;l'
1\n2$
- 值得注意的是,我们的命令在N读取完2就结束了.如果想继续,可能需要重启script的命
令,比如D
$ seq 3 | sed -n 'N;l;D'
1\n2$
2\n3$
Chapter 16: Access HoldSpace -hH gG x