其实"a"、"i"和"c"命令的TEXT部分写法是比较复杂的,如果TEXT只是几个简单字符,如上即可。但如果要TEXT是分行文本,或者包含了引号,或者这几个命令是写在"{}"中的,则上面的写法就无法实现。需要使用符号"\"来转义行��符号,这表示开启一个新行,此后输入的内容都是TEXT,直到遇到引号或者";"开头的行时。
例如,在a.sh的#!/bin/bash行后添加一个注释行"# Script filename: a.sh"以及一个空行。由于是追加在尾部,所以使用"a"命令。
sed '\%#!/bin/bash%a\# Script filename: a.sh\n' a.sh
"a"命令后的第一个反斜线用于标记TEXT的开始,"\n"用于添加空白行。如果分行写,或者"a"命令写在大括号"{}"中,则格式如下:
sed '\%#!/bin/bash%a\
# Script filename: a.sh\n
' a.sh
sed '\%#!/bin/bash%{p;a\
# Script filename: a.sh\n
;p}' a.sh
最后需要说的是,这3个命令的TEXT是存放在内存中的,不会进入模式空间,因此不受"-n"选项或某些命令的影响。此外,这3个命令依赖于输出流,只要有输出动作,不管是空输出流还是非空的输出流,只要有输出,这几个命令就会半路"劫杀"。如果不理解这两句话,这3个命令的结果有时可能会比较疑惑。
例如,"a"命令是追加在当前匹配行行尾的,但为什么下面的"haha"却插入到匹配行"def"的前面去了呢?
echo -e "abc\ndef\nxyz" | sed '/def/{a\
haha
;N}'
abc
haha
def
xyz
阅读了下面的"N"命令之后,再回头看这个示例,应该能知道为什么。
(9).多行模式命令"N"、"D"、"P"简单说明。
在前面已经解释了"n"、"d"和"p"命令,sed还支持它们的大写命令"N"、"D"和"P"。
"N"命令:读取下一行内容追加到模式空间的尾部。其和"n"命令不同之处在于:"n"命令会输出模式空间的内容(除非使用了"-n"选项)并清空模式空间,然后才读取下一行到模式空间,也就是说"n"命令虽然读取了下一行到模式空间,但模式空间仍然是单行数据。而"N"命令在读取下一行前,虽然也有自动输出和清空模式空间的动作,但该命令会把当前模式空间的内容锁住,使得自动输出的内容为空,也无法清空模式空间,然后读取下一行追加到当前模式空间中的尾部。追加时,原有内容和新读取内容使用换行符"\n"分隔,这样在模式空间中就实现了多行数据。即所谓的"多行模式"。 另外,当无法读取到下一行时(到了文件尾部),将直接退出sed程序,使得"N"命令后的命令不会再执行,这和"n"命令是一样的。
"D"命令:删除模式空间中第一个换行符"\n"之前的内容,然后立即回到SCRIPT循环的顶端,即进入下一个SCRIPT循环。如果"D"删除后,模式空间中已经没有内容了,则SCRIPT循环自动退出进入下一个sed循环;如果模式空间还有剩余内容,则继续从头执行SCRIPT循环。也就是说,"D"命令后的命令不会被执行。
"P"命令:输出模式空间中第一个换行符"\n"之前的内容。
"N"、"D"和"P"命令作用非常大,它们是绝佳的组合命令,因为借助它们能实现"窗口滑动"技术,这对于复杂的文本行操作来说大有裨益。但显然,这不是本文的内容。
此处按照惯例,还是给出它们的大致循环结构:其中"N"命令的if判断和前文的"n"一样,在本文结尾证明。
# "N"命令的大致循环结构
for ((line=1;line<=last_line_num;++line))
do
read $line to pattern_space;
while pattern_space is not null
do
execute cmd1 in SCRIPT;
execute cmd2 in SCRIPT;
ADDR1,ADDR2{ # "N" command
if [ "$line" -ne "$last_line_num" ];then
lock pattern_space;
auto_print;
remove_pattern_space;
unlock pattern_space;
append "\n" to pattern_space;
read next_line to pattern_space;
else
auto_print;
remove_pattern_space;
exit;
fi
};
……
auto_print;
remove_pattern_space;
done
done