sed学习笔记

来源:互联网 发布:ubuntu 壁纸 编辑:程序博客网 时间:2024/05/29 16:43
sed 与 awk 类似,也是面向行进行操作的;在 sed 开始处理输入流之前,会将输入读到buffer中
sed支持命令,因此以下说的退出 sed命令,并不是说退出 sed程序,而是退出sed的内部处理命令
可能产生循环的命令包括 t b D




sed 的命令尽量使用 ‘ 括起来,这样可以仅用 shell 扩展


sed -e '1d' /etc/services | more # 指定删除第一行
sed -e '1!d' /etc/services | more # 指定不删除第一行,其余的都删除
sed -e '1,10d' /etc/services | more # 指定删除第一行到第十行
sed -e '1,$d' /etc/services | more # 指定删除第一行到最后一行




sed -e '/regex/d' /etc/services | more # 指定删除符合 regex 的行


sed -n -e '/regex_start/,/regex_end/p' /path/to/my/test/file | more
取 符合regex_start 模式作为第一行,符合 regex_end 模式的作为最后一行,执行命令,这里的命令是输出第一行到最后一行的内容,其他的不输出
-n 除非明确要求输出,否则不进行输出;p命令用来要求 sed 对符合条件的内容进行输出,就这样,将只打印匹配部分。


sed -e -n '=;p' myfile | more # = 和 p 都是 sed 的命令,= 是通知 sed 打印行号,p 是通知sed打印当前行。如果 sed 有多个命令,可以使用 ; 作为各个命令的分割符


sed 的 s/// 命令,例如:
fe1: sed -e '1,10s/searchRegex/replaceString/g' myfile # 在 第1~10行使用 searchRegex 进行匹配,匹配成功的部分使用 replaceString 进行替换
fe2: sed -e '/^$/,/^END/s/searchRegex/replace/g' myfile3 # 在第一个空行到空行后第一个内容为 END 的行之间,使用 searchRegex 进行匹配,匹配成功的部分使用 replaceString 进行替换
fe3: sed -e 's/<[^>]*>//g' myfile.html # 替换掉html文件中的标签,/g 用来描述在正行进行匹配,如果不指定 /g 则只会替换一次
s/// 命令中的 / 可以使用 : # \\ 等来替换
# 在每一不含有"baz"的行中用"bar"替換(找並替換)foo"
sed '/baz/!s/foo/bar/g'


& 是 sed-regex 的原字符,他表示 regex 所匹配的内容
fe1: sed -e 's/.*/line-start: &/' myfile # 将在 myfile 的没一行前加上 line-start
sed 支持匹配结果的分组,使用 \N 来表示匹配的 N 个组,每个组在 () 内,使用 \0 表示匹配的全部


sed 的整行操作命令: i a c
fe1: sed -e 'i\insert this before each line' myfile
fe2: sed -e 's\append this before each line' myfile
fe3: sed -e 's\change each line to this' myfile




regex 在 sed 中的字符类:
字符类 描述
[:alnum:] 字母数字 [a-z A-Z 0-9]
[:alpha:] 字母 [a-z A-Z]
[:blank:] 空格或制表键
[:cntrl:] 任何控制字符
[:digit:] 数字 [0-9]
[:graph:] 任何可视字符(无空格)
[:lower:] 小写 [a-z]
[:print:] 非控制字符
[:punct:] 标点字符
[:space:] 空格
[:upper:] 大写 [A-Z]
[:xdigit:] 十六进制数字 [0-9 a-f A-F]






sed 有两个存储区: pattern space 和 hold space
pattern space 在每读入一行时,都会被清空,除了 g G x d D 等命令,其他命令是不会修改 pattern space
hold space 即使读入很多行,hold space 也不会被修改,除了 h H x 等命令,都不会修改 hold space
p 和 P 不会清空 pattern space 或 hold space
i.e. pattern space 在每次退出 sed命令 后,在 sed 读入下一行时,会被清空,
而 hold space 不会在 sed 读入下一个行时被清空。sed命令是指 -e 中所指定的命令
sed 读入的一行时,会把结尾的 newline 去掉


sed 的 address[sed 的操作范围,也就是 sed 执行动作时所需满足的条件]:
1. sed -n -e '1p' myfile
2. sed -n -e '1~4p' myfile # first~step
3. sed -n -e '\#vi#=;p' myfile # /regex/ 可以使用 \%regex% 来替换,其中 % 可以是任何字符,i.e. 可以使用 a 来替换 %
4. sed -n -e '/VI/Ip' myfile # /regex/I 在使用正则表达式匹配时,忽略大小写
5. sed -n -e '5,10p' myfile # 打印 1 到 10 行
sed -n -e '5,+3p' myfile # 打印 5 到 5+3 行
sed -n -e '30,~2p' myfile # 打印 30 到 32 行,打印 30 到30后第一个能整除2的行
在使用 -e 参数时,-e 后只能接一个区域,i.e. 一个匹配条件,如果需要使用多个区域,就要使用多个 -e,或者使用 -f 或把每个区域拆分到不同的 sed 中,而后使用 | 进行连接 sed 命令,例如:
.sed 文件中的内容是:
/regex/ {
=
}
/regex2/ {
p
}
使用 sed -n -f .sed myfile 执行 sed 时,相当于 sed -e '/regex/=' -e '/regex2/p' myfile


\` 是buffer的开始,\' 是buffer的结尾
sed 的动作命令:
1. q[exit_code]
只支持单个地址范围,例如:
sed 10q # 相当于 head -n 10
sed -n -e '5q;p' # 将打印 4 行,到第 5 行时,满足 sed 的q条件,将退出sed,由于 p 没有任何范围,i.e. p 没有任何条件限制,因此会一直执行
sed -n -e '5q;p' | echo "haha" # 打印 0-4 行,并在最后打印 haha
sed -n -e '5q1;p' && echo "haha" # 打印 0-4 行,不会输出 haha,这是因为 sed 返回码为 1,&& 操作不允许执行 echo 了
2. d
如果该命令被执行,会导致清空 pattern space,并且停止执行 sed 的命令,返回到 sed ,
sed将根据当前处理的行号读入一行,重新开始执行 sed 的命令


如果 d 命令被执行,那么 d 后面的所有命令都不会被执行,例如 sed -e 'd' -e 'p' ,这里的 p 是不会被执行的


sed -e '/./{H;$!d;}' -e 'x;/main/!d;' main.c
# 查找包含 main 的段,段落之间以空行分割
# H命令的过程是把 hold space 中增加一个 newline,而后将 pattern space 中的内容拷贝到 hold space中。
# 第一个 -e ,指出
如果这行不是空行,则将这行的内容拷贝到hold space中,并删除这行,
由于执行了 d 命令,那么第二个 e 就不会被执行,并且 pattern space 中的内容已经被清空,
执行了 d 命令后,控制权交给了 sed,由于没有指定 -n ,sed 输出pattern spacese,由于执行了 d 后pattern space 什么也没有,所以什么也没有输出,
sed 判断是否已经读完了文件,如果读完了,就退出sed,如果未读完,则继续读入下一行,读入的内容将去掉结尾的\n;
如果该行是空行,则不会执行第一个 -e ,
由第二个 -e 来处理,x 命令将 hold space 和 pattern space 中的内容进行交换, pattern space中存储的就是刚读入的段落,
hold space 中存储的就是空行,也就是说 hold space 中什么也没有,相当于清空 hold space,
而后查找使用/main/查找pattern space 中是否包含 main,
如果不包含则执行 d 命令,
如果包含 main,则不执行 d 命令,
而后退出 sed命令, sed 在自动读入下一行前,
由于没有 -n,那么会将 pattern space 中内容输出到 stdout,而后在读入下一行。




sed '/./,$!d' # 刪除文件開頭部分中的所有空白行,i.e. 從有字符開始的行直到最後一行保留,其他刪除
3'. D
删除到模式空间中的第一行,如果删除后,模式空间中还有剩余行,则返回 D 之前的命令,重新执行,
如果 D 后,patten space 中没有任何内容,则将退出 sed命令,将控制权交给 sed,
在没有 -n 参数时,sed 会输出 pattern space 中的内容,如果最后一行没有被读入,
则 sed 继续读入下一行,并去掉结尾的 \n 放到 pattern space 中,继续执行 sed 命令
如果最一行已经读入,则退出 sed


D 命令会引起循环,如果 D 被执行,那么 D 后面的命令是不会被执行的
例如 literal 文件是
a
b
c


sed -n -e 'p' -e '$!N;=;$p;D;p' literals
# 1. D 后的命令 p 不会被执行
处理结果是
a # line 1
2 # line 2
b # line 3
3 # line 4
b # line 5
c # line 6
c # line 7
3 # line 8
c # line 9
下面一点一点解释 sed -n -e 'p' -e '$!N;=;$p;D;p' literals :
line 1: 这个 a 是由第一个 p 打印出来的,这时的patter space 是 [a]
line 2: 2 是由 = 打印出来的,打印 2 是因为 $!N 读入了下一行,这是 pattern space 是 [a\nb]
由于当前是第 2 行,$p 不会有输出;而后执行 D ,删除后 pattern space 是 [b],
由于有剩余,因此会从第一个 -e 'p' 处执行,也就是说 D 命令会产生循环。
D后的p不会被执行
line 3: 这个 b 是由第一个 p 打印出来的,证明了前次执行完 D 命令后的patter space 是 [b],与在 line 2 的 D 后没有变换
line 4: 3 是由 = 打印出来的,打印 3 是因为 $!N 读入了下一行,这时 pattern space 是 [b\nc],可以在下一行解释证明这一点
line 5,6: 这两行是由 $p 打印出来的,由于当前是 第三行,也就是 $ 行,p 命令会打印模式空间的内容。
而后执行而后执行 D ,删除后 pattern space 是 [c],
由于有剩余,因此会从第一个 -e 'p' 处执行,也就是说 D 命令会产生循环。D后的p不会被执行
line 7: 这个 c 是有第一个 p 打印出来的,证明了前次执行完 D 命令后的patter space 是 [c]
line 8: 3 是由 = 打印出来的,已经到了 $ ,因此 $!N 不会读入任何内容
line 9: 这个9是由 $p打印出来的,也进一步证明了 $!N 没有执行,而后执行完 D 命令后,
D 后 pattern space 中没有任何内容,结束本次的处理,把控制权交给了sed。sed 检查当前执行到
了最后一行,也完成了处理,退出 sed 命令,整个过程中, sed 命令没有自动读入一行,都是由手动的
$!N 读入的,


sed -n -e '/regexp/{=;x;1!p;g;$!N;p;D;}' -e h # 模拟 grep -A1 -B1


sed '$!N; /^\(.*\)\n\1$/!P; D'
# 刪除文件中重複的連續的行(似於"uniq"命令)
# 重複行中第一行保留,其他刪除
# 这里只需要注意查找重复行的 regex 为 ^\(.*\)\n\1$


sed -e '$!N;$!P;$!D;$d'
# 刪除一個文件中最後2行
# a: $!N 当前处理的不是最后一行,那么就把下一行读进来,而后当前处理的行号 + 1,
# $!P 如果当前处理的行号是最后一行就不会打印,
# $!D 如果当前处理的行号不是最后一行就删除 pattern space 的的第一行,
# 如果 pattern space 中还有内容,就重新执行命令,转 a,在 D 后,pattern space 中
# 始终会有内容
# $d 如果当前是最后一行,则删除pattern space 中的内容,不用使用 $d,因为 $d 前面的是
# $!D,因此,如果能够执行到 d ,那么一定是 $


# 刪除文件中除一行空白行之外的所有連續空白行,也同時刪除所有從頭到尾的所有空白行(似於"cat -s")
sed '/./,/^$/!d' # method 1, allows 0 blanks at top, 1 at EOF
sed '/^$/N;/\n$/D' # method 2, allows 1 blank at top, 0 at EOF




4. p
打印模式空间的内容,在没有 -n 参数时,-e 的任何参数,都将附加 ;p,e.g.:
sed -e '1,10d' myfile 相当于 sed -e '1,10d;p' myfile
在存在 -n 参数时,如果 p 命令的同范围的命令中,在 p 前的命令没有 d 命令,那么只有满足了 p 的范围的内容,才会被打印
sed -n -e '/main/!p/ main.c # 如果行中不包含 main ,则打印这行,如果包含 main 则不打印这行c
4'. P
打印模式空间的第一行
sed -n -e 'h;G;P' main.c # 经过 h;G; 后,模式空间的内容已经是本次读入的行的两行了,但 P 只会打印一行,而 p 会打印两行
5. n
If auto-print is not disabled, print the pattern space, then, regardless,
replace the pattern space with the next line of input. If there is no more input then
sed exits without processing any more commands.
执行到这个命令时,如果没有 -n 选项;输出当前 pattern space 中的内容,无论是否有-n,将读入下一行的内容来替换 pattern space 中的内容
所谓执行到这个命令,是指与 n 同范围的命令中,在 n 的前面没有 d 或 q


sed -n '/regexp/{n;p;}' # 打印在"regexp"之後緊跟那一行,但是不打印包含"regexp"的行.
4'. N
在当前的 pattern space 中添加一个 newline 标识符,并且读入一行 append 到 patten space 中
注意:无论是 sed 自动读到 pattern space 中的内容,还是通过 n N 读入到 pattern space 中的内容,都会去掉结尾的 newline,newline 包括 linux 的 \n 或 windows 的 \r\n
$!N 如果不是最后一行,就读入新行附加于pattern space之后,
Add a newline to the pattern space, then append the next line of input to the pattern space.
If there is no more input then sed exits without processing any more commands.
如果N命令没有读入任何内容,sed 就会退出,不会继续处理 N 后的 sed命令
在 gnu sed 4.1.5 中,如果没有 -n,会输出 pattern space 中的内容,而后在退出 sed
在 gnu sed 4.1.5 中,可能会直接退出 sed,不会输出 pattern space中 的内容
5. =
执行 = 命令时,不会修改 pattern space,而是直接把 行号+newline 打印到输出中
注意: = 命令操作的不是 pattern space,而是直接把行号打印到到输出
例如: sed -e '=;d' main.c # 打印完行号后,删除 pattern space 中的内容
sed -e 'd;=' main.c # 不会输出任何内容,因为 = 不会被执行
sed -n -e '=' main.c # 由于 -n 参数,因此只输出行号,而不是出 pattern space 中的任何内容
sed -n -e 'p;=;p' main.c # 可以看出 = 没有动过 pattern space,而是直接把 pattern space 中的内容打印到输出中
6. {action_1;action_2;...;action_N}
执行满足范围的一组命令,e.g. 比较 sed -n -e '1,2{=;p} myfile 与 sed -n -e '1,2=;p' myfile 输出内容的不同,要求myfile > 2行
在{}中可以继续插入条件,例如:
a
b


c
使用 sed -n -e '/./{$d;p}' file,就会过滤掉最后一行,并且不打印空行


7. 替换命令 s/regexp/replacement/flags,s也就是一个命令,e.g.
sed -n -e '1,5{s/while/WHILE/g;p}' myfile # 将 1-5 的while替换成WHILE,并且打印这 5 行
sed -n -e 's/\(v\+\)i/\1/gp' # 如果包含vvi,则认为这是一个输入错误,用户本意输出的是vv,而非vi,这时可以使用以上命令进行替换,e.g. 录入 vvvvvi,就会显示vvvvv
如果存在多个元组,则可以在 \$ 来匹配最后一个元组
如果有元组的话, & 相当于 \0,如果没有的话,& 将匹配所有匹配的内容,可以在/// 的第2个/和第3个/之间 使用 & 来取匹配的文本
flags的取值
a. g 整行全部替换,默认为只替换第一个匹配项目
b. number 与 g 类似,但只替换 number 次,e.g. sed -n -e 's/vi/VI/2p' myfile,把内容的vi,匹配不超过两次,匹配成功的使用VI来代替
c. p 打印参数,这个 p 与 p 命令类似,但这个是 s 命令的参数
8. a i c 命令,a\TEXT i\TEXT c\TEXT
a 命令是在指定的范围内,输出范围内的每一行,并且在每一行后加上换行符,而后输出TEXT
i 命令与 a 命令类似,但不是 append ,而是 insert
c 命令与 a 命令相似,但不是 append ,而是 changeTo
sed -e '1,3c\changeToMyString' myfile
sed -e '/regex/a\changeToMyString' myfile
9. t 命令
类似于 jumpCondition,Condition来源于前一个 s 命令是否发生了替换,
一般无须用到这个命令,如果使用到这个命令就要考虑 awk 来解决问题了
这个例子来源于http://www.yesadmin.com/355/130317/index.html,
file 文件的内容:
a
b
=c
d
执行 sed -e :a -e '$!N;s/\n=/ /;ta' -e 'P;D' file 命令后将输出
a
b c
d
下面一点一点说明 sed -e :a -e '$!N;s/\n=/ /;ta' -e 'P;D' file
其实这个例子是一个很复杂的命令,涉及两个一重循环,file.line1 代表 file 文件的第一行,
result.line2 代表 执行结果的第 2 行,即2 3,processing.line = n 代表当前处理的是第 n 行


sed 开始时,读入第一行到 pattern space=[a],而后在 -e '$!N...." 加入 a 标签,
注意 :a 不是在读入的内容中加入标签,而是在命令部分加入标签,而后执行第二部分 -e ,
由于 proecessing.line = 1,因此$!N会被执行,执行后 pattern space=[a\nb],processing.line=2,
执行 s 命令,s 命令匹配失败,ta不会被执行,而后执行地三个 -e ,P 将打印 pattern space 的第一行,
也就输出了 result.line1,而后执行 D,执行后 pattern space=[b],而后执行第一个 -e,
还是在 -e '$!N...." 加入 a 标签,由于 processing.line=2,因此 $!N 会被执行,$!N执行后,
pattern space=[b\n=c],processing.line=3,而后执行 s 命令,s 命令匹配成功,pattern space
被修改成 [b c],由于 s 替换成功,ta被执行,命令的执行点转到了 '$!N;s/\n=/...'处,
执行 $!N,由于 processing.line=3,因此会执行 $!N,执行完 $!N 后,pattern space=[b c\nd],
processing.line=4,而后执行 s 命令,s 命令匹配失败,没有进行任何替换,不会执行 ta,而后执行第三个 -e,
P 命令输出 result.line2,而后执行 D 命令,pattern space=[d],由于 pattern space 有剩余,因此会转到
第一个 -e 处,:a 重新设置标签,由于processing.line=4,因此 $!N 不会被执行, pattern space 不变,
pattern space=[d],因此 s 命令不会匹配失败,不会发生替换,由于没有发生替换,ta 也就不会被执行,
而后执行地三个 -e ,P 命令输出 result.line3,而后执行 D 命令,pattern space=[],由于 pattern space 没有剩余,
因此会退出 sed 的命令处理,控制权由 sed 来接管,sed 检查到 processing.line=$,因此会退出 sed


需要注意的是 ta 时,这个 a 标签要出现在 t 命令之前


可以加入输出行号,来证明以上解释是正确的:
sed -n -e :a -e ';$!N;=;s/\n=/ /;ta' -e 'P;D' file
2
a
3
4
b c
4
d
sed -n -e :a -e ';$!N;=;p;s/\n=/ /;ta' -e 'P;D' file
2
a # 第二个 -e 的 p 输出
b # 第二个 -e 的 p 输出,这两处由那个新加入的 p 一次打印出来的,说明 $!N后 pattern space=[a\nb]
a # 由第三个 -e 中的 P 输出
3
b # 第二个 -e 的 p 输出
=c # 第二个 -e 的 p 输出,这两处由那个新加入的 p 一次打印出来的,说明 D 使得 pattern space=[b],$!N后 pattern space=[b\n=c]
4
b c # 第二个 -e 的 p 输出,s执行了替换,并且 ta 执行了,确实跳转了,没有被第三个 -e 的 D 删除掉 pattern space=[b c]中的内容
d # 第二个 -e 的 p 输出,说明 $!N 执行后,pattern space=[b c]
b c # 由第三个 -e 中的 P 输出
4
d # 第二个 -e 的 p 输出
d # 由第三个 -e 中的 P 输出


还有一个有趣的例子,按照 30 列宽排列所有文本
sed -e :a -e 's/^.\{1,29\}$/ &/;ta' myfile
myfile 的内容是
aaa
123456789012345678901234567890a
bbb
将输出的是
aaa
123456789012345678901234567890a
bbb
讨论一下这个例子,-e :a -e 's/$.\{1,29\}/ &;ta'
首先在第二个 -e 前设置一个label,而后搜索当前行,如果当前行的长度小于30,
那么将在当前行前加一个空格,而后跳转到 a 标签处,也就是说再次执行第二个 -e 部分,
.....,可以看出 ta 这个跳转实现的功能就是将长度不足 30 的行的开头插入空格,
如果该行的长度 >29,不做任何操作,因此实现了右对齐的功能。
可以增加一个 p 来显示 ta循环 的处理过程:
sed -e :a 'p;s/^.\{1,29\}/ &;ta' myfile


可以使用 sed -e :a -e 's/^.\{1,27\}$/ & /;ta' myfile 来完成居中对齐的功能


sed -e :a -e 's/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/;ta' numberfile # 使用 ta 给数字增加千分位


9'. b 命令
也是一个跳转命令,与 t 不同的是,这个命令是无条件跳转


sed -e :a -e '$q;N;11,$D;ba' # 模拟 tail -n 10,这里的 D 命令的范围是 11 行到最后一行


# 刪除文件結尾部分中的所有空白行
sed -e :a -e '/^\n*$/{$d;N;ba' -e '}'
# 如果当前 pattern space 中是空行 或 只有 \n*,则执行$d,如果$d执行了,则退出 sed命令,因为已经到了 $,因此 退出sed,
如果不是 $,则 N 读入下一行,而后重新开始匹配 /^n*$/,如果前一个 N 读入的不是空行,那么就打印pattern space中的内容,否则
继续执行 $d,如果$d执行了.... 这说明只有在结尾处存在大量的空行时,才能执行到$d,如果文本的段落中间存在多个空行,不会删除这些空行


在 sed 4.1.5 中,如果在 $ 行执行 N,那么退出 sed命令,而后 sed 打印 pattern space,因此
sed -e :a -e '/^\n*$/N;/\n$/ba' 不能去掉结尾空行,如果在 $ 行执行 N,那么退出 sed命令,并且不打印 pattern space,
那么 sed -e :a -e '/^\n*$/N;/\n$/ba' 可以去掉结尾空行


# 刪除一個文件中後10行
sed -e :a -e '$d;N;2,10ba' -e 'P;D' # method 1
# 如果文件长度小于 10 行,由于 N;2, 10ba,会不但读入,到文件还没有读入10行,就执行了$d,
# 因此不会有任何输出,也就是不会执行到第二个 -e 处
# 如果文件长度大于 10 行,假设为 11 行:N;2,10ba,会一直循环到把前 10 行的内容全部读入到 pattern space
# 为止,在读入第 10 行后,依然 ba,这样重新执行 '#d;N;2,10ba,因为当前是 10 行,因此 $d 不会执行,而后
执行 N ,处理的行号加 1,变成了 11,这时 2,10ba 不会被执行,执行第二个 -e,首先 P 打印第一行,而后 D 删除的一行,
D 执行后,由于pattern space 中还有内容,因此转到命令的开始处,执行 $d;N;2,10ba,这是由于处理的是11,即 $,
因此 $d 会被执行,这样 pattern space中的内容就被删除了,而后退出 sed命令, 由于已经读入了 $,因此退出 sed程序


# 刪除一個文件中後10行
sed -n -e :a -e '1,10!{P;N;D;};N;ba' # method 2
# 如果文件长度小于 10 行,由于 1,10!{P;N;D},当读到最后一行 x 行时,也不会执行 1,10!{P;N;D},退出 sed命令,
# 由于存在 -n 选项,因此不会 sed 不会输出 pattern space,由于已经到了 x 行,即 $,所以会退出 sed
# 如果文件长度大于 10 行,假设为 12 行:则在第 1 到 10 行,1,10!{P;N;D} 不会被执行,当执行到第 10 行时,继续
# 执行 N ,读入 11 行到 pattern space,并且当前处理的行号加 1,则处理的是 11 行,而后 ba 到命令起始处重新执行,
# 由于是 11 行,因此 1,10!{P;N;D;}会被执行,这时 P 打印出 pattern space 的第一行,而后 N,读入 12 行到
pattern space,并且当前处理的行号加 1,则处理的是 12 行,则 D 后会删除 pattern space 的的一行,由于
pattern space 中还有其他内容,因此会转到命令起始处重新执行1,10!{P;N;D},由于当前是 12 行,因此,可以执行
1,10!{P;N;D},这时 P 打印出 pattern space 的第一行,而后 N 读入,由于当前是 $,因此 N失效,退出 sed 命令,
由于存在 -n,因此 sed 不会打印 pattern space 中内容,由于当前是 $,因此退出 sed 程序




10. h H 命令
h 将 hold space 中的内容使用 pattern space 中的内容来替换
H 将 hold space 中的内容使用 加上一个 trailing newline,而后 append pattern space
11. g G 命令
g 将 pattern space 中的内容使用 hold space 中的内容来替换
G 将 pattern space 中的内容使用 加上一个 trailing newline,而后 append hold space


sed -n '/regexp/{g;1!p;};h' # 打印包含"regexp"那一行的上一行,但是不打印包含"regexp"的行.
12. x 命令
将 pattern space 与 hold space 中的内容交换
fe: sed -n '/^$/{p;h;};/./{x;/./p;}' # 刪除每個段落中最後1行




一个简单的 prog.sed 文件
/.*f().*/ {
i\insertMyStringBeforeLine
}


不太常见的脚本
sed -e 'G' main.c # 把没行的内容后都加一个空行,加空行的原因是 hold space 中什么也没有
sed -e 'h;G' main.c # 双行打印 main.c
sed -e 'H;$G' main.c # 会将 maic.c 的内容打印两遍
sed '/regex/{x;p;x;}' # 由于默认 hold space 为null,第一个 x 交换后,pattern space 就是null了,
# 所以 p 打出的是空行,而后再 x ,重新把原来的内容读入,在没有 -n 选项 和 d 命令时,
# 默认会输出内容,因此将输出原行内容。表现出的结果就是在每個含有字符串regex的行上插入一行空白行。




麻烦的脚本,这个脚本会让第二行的内容输出到匹配的文本的结尾
# Put 80 spaces in the buffer
2 {
x
g # 保证第二行的内容也被打印出来;先 x 而后 g ,可以使用 h 来替换
}
$G
# 将要匹配的文本 main.c 是
# void f() {
# printf("%d", 1);
# } // f
#
# main() { main main
# int i=0;
# } // main
# 将要输出的文本是
# void f() {
# printf("%d", 1);
# } // f
#
# main() { main main
# int i=0;
# } // main
# printf("%d", 1);




行号相关的脚本:


sed = main.c | sed -e 'N;s/\n/ ' # sed = filename 的功能是 相当于 sed '=' main.c
# 而后使用 N 命令,在 pattern space 中插入一个 newline,
# 并将读入下一行的内容 append 到 patter space 中


行号操作


sed -n '$=' myfile # 用于统计行数,类似于 wc -l
sed '/./=' file | sed '/./N; s/\n/ /' # 打印非空行的行号 和该行内容
sed -e '/^$/d;=' file | sed -e 'N;s/\n\|\r\n/ /' # 打印非空行的行号和该行内容
# 如果改行内容是空行 ^$ ,那么将执行 d 命令,也就不会执行 = 命令
sed -e '/^[ \t]*//' file # 去掉行首的空格和制表符
sed -e '/[ \t]*$//' file # 去掉行尾的空格和制表符


模仿 tac 命令:
sed -n -e '1!G;h;$p' test.txt
sed -e '1!G;h;$!d' test.txt
0 0