sed系列文章:html
sed修炼系列(一):花拳绣腿之入门篇
sed修炼系列(二):武功心法(info sed翻译+注解)
sed修炼系列(三):sed高级应用之实现窗口滑动技术
sed修炼系列(四):sed中的疑难杂症git
"(注:)"
,为本人自行加入,助于理解和说明,非原文内容。PS:直到翻译完,才发现加了不少不少我我的注释,尽管是一篇译文,但也舍不得删掉这些感想。若是这些"注:"有碍观感,还望各位能体谅。1 简介
2 调用方式
3 sed程序
3.1 sed是如何工做的
3.2 sed定址:筛选行的方式
3.3 正则表达式一览
3.4 sed经常使用命令
3.5 sed的s命令
3.6 比较少用的sed命令
3.7 大师级的sed命令(sed标签功能)
3.8 GNU sed特有的命令
3.9 GNU对正则表达式的反斜线扩展
4 一些简单的示例脚本
5 GNU sed的限制和优势
6 学习sed的其余资源
7 Bugs说明(建议看) 正则表达式
sed是一个流式编辑器。流式编辑器用于对输入流(文件或管道传递的数据)执行基本的文本转换操做。在某些方面上,sed有点相似于脚本化编辑的编辑器(如ed
),但sed只能经过一次输入流,所以它的效率要更高。但sed区别于其余类型编辑器的地方在于它能筛选过滤管道传递的文本数据。
(注:sed只能经过一次输入流,意味着每次的输入流只能处理一次,所以像(sed -n '2{p;q}';sed -n 3{p;q}) <filename
这样的命令中,第二个sed语句读取的输入流是空流。)算法
一般,会使用下面的方式来调用sed: shell
sed SCRIPT INPUTFILE...
完整的调用格式为:express
sed OPTIONS... [SCRIPT] [INPUTFILE...]
若是不指定INPUTFILE或者指定的INPUTFILE为"-",sed将从标准输入中读取输入流并进行过滤。SCRIPT
是第一个非选项参数,sed仅在没有使用"-e"或"-f script_file"选项时才将其看成是script部分而非输入文件。编程
可使用下面的命令行选项来调用sed:c#
'--version'
输出sed的版本号并退出。bash
'--help'
输出sed命令行的简单帮助信息并退出。markdown
'-n'
'--quiet'
'--silent'
默认状况下,sed将在每轮script循环结束(How 'sed' works: Execution Cycle)时自动输出模式空间中的内容。该选项禁止自动输出动做,这种状况下,只有显式经过"p"命令来产生对应的输出。
'-e SCRIPT'
'--expression=SCRIPT'
向SCRIPT中添加命令集,让sed根据这些命令集来处理输入流。(注:其实就是指定pattern和对应的command)
'-f SCRIPT-FILE'
'--file=SCRIPT-FILE'
指定包含command集合的script文件,让sed根据script文件中的命令集处理输入流。
'-i[SUFFIX]'
'--in-place[=SUFFIX]'
该选项指定要将sed的输出结果保存(覆盖的方式)到当前编辑的文件中。GNU sed是经过建立一个临时文件并将输出写入到该临时文件,而后重命名为源文件来实现的。
该选项隐含了"-s"选项。
当到达了文件的尾部,临时文件被重命名为源文件的名称。若是还提供了SUFFIX,则在重命名临时文件以前,先使用该SUFFIX先修改源文件名,从而生成一个文件备份。
(注:临时文件老是会被重命名为源文件名称,也就是说sed处理彻底结束后,仍使用源文件名的文件是sed修改后的文件。文件名中包含了SUFFIX的文件则是最原始文件的备份。例如源文件为a.txt,sed -i'.log' SCRIPT a.txt
将生成两个文件:a.txt和a.txt.log,前者是sed修改后的文件,a.txt.log是源a.txt的备份文件。)
规则以下:若是扩展名不包含符号"*",将SUFFIX被添加到原文件名的后面看成文件后缀;若是SUFFIX中包含了一个或多个字符"*",则每一个"*"都替换为原文件名。这使得你能够为备份文件添加一个前缀,而不是后缀,甚至能够将此备份文件放在在其余已存在的目录下。
若是没有提供SUFFIX,源文件被覆盖,且不会生成备份文件。
'-l N'
'--line-length=N'
为"l"命令指定默认的换行长度。N=0意味着彻底不换行的长行,若是不指定,则70个字符就换行。
'--posix'
GNU 'sed' includes several extensions to POSIX sed. In order to simplify writing portable scripts, this option disables all the extensions that this manual documents, including additional commands. Most of the extensions accept 'sed' programs that are outside the syntax mandated by POSIX, but some of them (such as the behavior of the 'N' command described in *note Reporting Bugs::) actually violate the standard. If you want to disable only the latter kind of extension, you can set the 'POSIXLY_CORRECT' variable to a non-empty value.
'-b'
'--binary'
This option is available on every platform, but is only effective where the operating system makes a distinction between text files and binary files. When such a distinction is made--as is the case for MS-DOS, Windows, Cygwin--text files are composed of lines separated by a carriage return and a line feed character, and 'sed' does not see the ending CR. When this option is specified, 'sed' will open input files in binary mode, thus not requesting this special processing and considering lines to end at a line feed.
'--follow-symlinks'
该选项只在支持符号链接的操做系统上生效,且只有指定了"-i"选项时才生效。指定该选项后,若是sed命令行中指定的输入文件是一个符号链接,则sed将对该符号连接的目标文件进行处理。默认状况下,禁用该选项,所以不会修改连接的源文件。
'-r'
'--regexp-extended'
使用扩展正则表达式,而不是使用默认的基础正则表达式。sed所支持的扩展正则表达式和egrep
同样,使用扩展正则表达式显得更简洁,由于有些元字符不用再使用反斜线"\",但这是GNU扩展功能,所以应避免在可移植性脚本中使用。
'-s'
'--separate'
默认状况下,sed会将命令行中指定文件的全部行看成一个长输入流。此选项为GNU sed扩展功能,指定该选项后,sed将认为命令行中给定的每一个文件都是独立的输入流。所以此时范围定址(如/abc/,/def/
)没法跨越多个文件,行号也会在处理每一个文件时重置,"$"表明的是每一个文件的最后一行,"R"命令调用的文件将绕回到每一个文件的开头。
'-u'
'--unbuffered'
使用尽可能少的空间缓冲输入和输出行。(该选项在某些状况下尤其有用,例如输入流的来源是"tail -f
时,指定该选项将能够尽快返回输出结果。)
'-z'
'--null-data'
'--zero-terminated'
以空串符号"\0"而不是换行符"\n"做为输入流的行分隔符。
若是未给定"-e"、"-f"、"--expression"或"--file"选项,则命令行中的第一个非选项参数将被认为是SCRIPT被执行。
若是命令行中在上述选项后还加了任意参数,则都将被看成输入文件。其中"-"表示的是标准输入流。若是没有给定任何输入文件,则默认从标准输入中读取输入里流。
sed程序由一个或多个sed命令组成(注:请勿将sed这个工具理解为sed命令,而应该看做是一个包含不少命令的程序,因此后文中"sed命令"表示的是sed中的子命令,而非sed自己这个命令),这些命令经过"-e"、"-f file"传递,或者当没有指定这两个选项时,经过第一个非选项参数传递。这些传递的命令集合称为sed的执行脚本(SCRIPT),命令的执行顺序按照命令行中给定的顺序依次执行。
在SCRIPT或SCRIPT-FILE中的多个命令可使用分号";"进行分隔,或者换行书写。但有些命令,因为它们的语法问题,致使不支持使用分号做为命令分隔符,所以只能换行书写,除非它们是SCRIPT或SCRIPT-FILE中的最后一个命令。容许命令的前面有空白字符。
每一个sed命令由地址或范围地址,随后紧跟单字符表明的命令名称组成,其中地址部分能够省略。
sed维护两个数据缓冲空间:一直处于活动状态的模式空间(pattern space)和辅助性的保持空间(hold space)。这两个空间初始时都为空。
sed经过执行下面的循环来操做输入流中的每一行: 首先,sed读取输入流中的一行,移除该行的尾随换行符,并将其放入到pattern space中。而后对pattern space中的内容执行SCRIPT中的sed命令,每一个sed命令均可以关联一个地址:地址是一种条件判断代码,只有符合条件的行才会执行相应的命令。当执行到SCRIPT的尾部时,除非指定了"-n"选项,不然pattern space中的内容将写到标准输出流中,并添加回尾随的换行符。而后进入下一个循环开始处理输入流中的下一行。
除非指定了特殊的命令(例如"-D"),不然pattern space中的内容在SCRIPT循环结束后会被删除。可是hold space会保留其中的内容(参见sed命令'h', 'H', 'x', 'g', 'G'以获知两个buffer空间是如何互相移动数据的)。
(
注:也就是说,sed程序工做时包含内外两个循环:内循环是SCRIPT循环,循环的过程是对模式空间中的内容执行SCRIPT中的命令集合,还包括一个隐含的自动输出动做用于输出模式空间的内容到标准输出中;外循环是sed循环,读取下一行到模式空间中,并执行SCRIPT循环,SCRIPT循环结束后再读取下一行到模式空间中。使用编程结构描述,结构以下:
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;
……
auto_print;
remove_pattern_space;
done
done
通常状况下,SCRIPT循环都只执行一轮就退出并进入外层sed循环,由于执行完一次SCRIPT循环, pattern space就清空了,但有些特殊命令(如"D"命令)会进入多行模式,使得SCRIPT循环结束 时将数据锁在pattern space中不输出也不清空,甚至在SCRIPT循环还没结束时就强行进入下一 轮SCRIPT循环,在《sed & awk》一书中,这种行为被称之为"回到SCRIPT的顶端"。其实就至关 于在上面的while循环结构中加上了"continue"关键字。此外还有命令(如"d")能够直接退出 SCRIPT循环进入下一个sed循环,就像是在while循环中加上了"break"同样。甚至还有直接退出 sed循环的命令(只有2个这样的命令:"q"和"Q"),就像是加上了"exit"同样。
auto_print
和remove_pattern_space
动做是隐含动做,分别称之为自动输出和清空pattern space。"-n"选项禁用的自动输出不是禁止隐含动做auto_print,而是让其输出空内容(它们是有区别的),并执行remove_pattern_space。只要没有指定"-n"选项,在SCRIPT循环结束时必定输出pattern space中的所有内容并清空pattern space,只不过某些特殊的命令能够将pattern space中的内容锁住使auto_print输出空内容,也使得remove_pattern_space移除不了pattern space的内容。
之因此要特意指出"输出空内容"并标明它和禁止输出动做,是由于某些命令(如"a"、"i"、"c"命令)依赖于输出流,没有输出动做就没有输出流,这些命令就没法正确完成。
这几个循环、几个动做的细节很是重要,虽不影响后文单个选项或命令的理解,但若是将命令结合,有时候的结果极可能会出人意料,而sed难就难在命令的合理组合。例以下面的命令,"a"命令原本是要将xyz插入到aaa所在行后面的,但结果却插在了aaa行的前面。
echo -e "aaa\nbbb" | sed '/aaa/{a\ > xyz > ;N}'
xyz
aaa
bbb
)
(注:sed SCRIPT中的命令由单字符表明的命令和地址组成,地址的做用是匹配当前正在处理的行,若是能匹配成功,就执行与该地址相关联的命令。地址由定址表达式决定,定址的结果多是单行,多是一个范围,省略定址表达式时表示全部行。)
可使用下面任意一种方式进行定址:
'NUMBER'
指定一个行号,sed将仅只匹配该行。(须要注意,除非使用了"-s"或"-i"选项,sed将对全部输入文件的行连续计数。)
'FIRST~STEP'
这是GNU sed的功能,FIRST和STEP都是非负数。该定址表达式表示从第FIRST行开始,每隔STEP行就再取一次。也就是取行号知足“FIRST+(N*STEP)” (其中N>=0)的行。所以,要选择全部的奇数行,使用“1~2”;要从第2行开始每隔3行取一次,使用“2~3”;要从第10行开始每隔5行取一次,使用“10~5”;而“50~0”则表示只取第50行。
'$'
该符号匹配的是最后一个文件的最后一行,若是指定了"-i"或"-s",则匹配的是每一个文件的最后一行。
(注:总之,"$"匹配的是每一个输入流的最后一行)
'/REGEXP/'
该定址表达式将选择那些能被正则表达式REGEXP匹配的全部行。若是REGEXP中自身包含了字符"/",则必须使用反斜线进行转义,即"\/"
。
空的正则表达式"//"表示引用最后一个正则表达式匹配的结果(命令"s"中的正则匹配部分若是是空正则"//",则同样如此处理)。注意,正则表达式的修饰符(即下文中的"I"和"M",以及s命令中的修饰符"i"、"I"和"M")是在正则表达式编译完成以后(注:进行数据匹配时)才生效的,所以,这些修饰符和空正则一块儿使用时无效(注:会报错,提示"cannot specify modifiers on empty regexp")。
(注:这里的修饰符特指定址时可用的修饰符,即I和M。命令s也有修饰符"i"、"I"和"M"。
如:sed '/hold/Is//gogogo/g'
能成功,由于第一个定址正则表达式的修饰符"I"的对象是非空集"hold",第二个正则模式"//"没有指定"i"、"I"或"M"修饰符,因此成功。但sed '/hold/Is//gogogo/gi'
会报错,由于在正则编译结束后还未开始进行匹配的时候,第二个正则表达式中的修饰符"i"的对象是空集。sed '/hold/I{//Mp}'
和sed '/hold/I{//Ip}'
也都是失败的,缘由都是第二个正则的修饰符对象是空集。
'\%REGEXP%'
('%'可使用其余任意单个字符替换。)
这和上一个定址表达式的做用是同样的,只不过是使用符号"%"替换了符号"/"。当REGEXP中包含"/"符号时,使用该定址表达式就无需对"/"使用反斜线"\"转义。但若是此时REGEXP中包含了"%"符号时,该符号须要使用"\"转义。
总之,定址表达式中使用的分隔符在REGEXP中出现时都须要使用反斜线转义。
'/REGEXP/I'
'\%REGEXP%I'
正则表达式的修饰符"I"是GNU的扩展功能,表示REGEXP在匹配时不区分大小写。
'/REGEXP/M'
'\%REGEXP%M'
正则表达式的修饰符"M"是GNU的扩展功能,这可让sed直接匹配多行模式下(multi-line mode)的位置。该修饰符使得正则表达式的元字符"^"和"$"匹配分别匹配每一个新行后的空字符和新行前的空字符。还有另外两个特殊的字符序列(\`和\',分别是反斜线加反引号,反斜线加单引号),它们老是匹配buffer空间中的起始和结束位置。此外,元字符点"."不能匹配多行模式下的换行符。
(注:在单行模式下,使用M和不使用M是没有区别的,但在多行模式下各符号匹配的位置将和一般的正则表达式元字符匹配的内容不一样,各符号的匹配位置以下图所示)
若是没有给定地址,则会匹配输入流中的全部行,若是给定了单地址的定址表达式,则这些行会在模式空间中被匹配条件进行匹配。
可使用逗号分隔的两个地址表达式(ADDR1,ADDR2)来描述范围地址。范围地址表示从符合第一个地址条件的行开始匹配,直到符合第二个地址的行结束。
(注:须要注意,不管行是否符合地址匹配条件,它们都会被读入pattern space,只不过读入的行若是不符合地址匹配条件,将直接执行SCRIPT循环中的隐含动做,并结束SCRIPT循环继续读取输入流的下一行)
若是第二个定址表达式是正则表达式REGEXP,将从符合第一个定址表达式的行开始逐行检查,以匹配范围定址的结束行:范围定址的范围老是会跨越至少两行(固然,若是输入流结束的情形除外)。
若是第二个定址表达式是一个小于或等于第一个表达式表明的行号值,则只会匹配第一个表达式搜索到的那一行。
(注:即若是范围地址的结束行在起始行的前面,则只会匹配起始行。)
GNU sed还支持如下几种特殊的两地址格式,这些都是GNU的扩展功能:
'0,/REGEXP/'
使用行号0做为起始地址也是支持的,就像此处的"0,/REGEXP/",这时sed会尝试对第一行就匹配REGEXP。换句话说,"0,/REGEXP/"和"1,/REGEXP/"基本是相同的。但以行号0做为起始行时,若是第一行就能被ADDR2匹配,范围搜索当即就结束,由于第二个地址已经搜索到了;若是以行号1做为起始行,会从第二行开始匹配ADDR2,直到匹配成功。
注意,0地址只有在这一种状况下才是有意义的,实际上并无第0行,在其余任什么时候候使用行号0都会给出错误提示。
'ADDR1,+N'
匹配ADDR1和其后的N行。
'ADDR1,~N'
匹配ADDR1和其后的行直到出现N的倍数行,倍数可为随意整数倍。 (注:能够是任意倍,只要N的倍数是最接近且大于ADDR1的便可。如ADDR1=1,N=3
匹配1到3行,ADDR1=5,N=4
匹配5-8行。而"1,+3"匹配的是第一行和其后的3行即1-4行。)
在地址的后面追加"!"符号指定反转匹配的含义。意思是,若是"!"跟在范围定址的后面,则那些匹配的行将不被选择,而是不匹配的行被选择。一样能够适用于单个定址甚至于有悖常理的空定址。
(注:例如,sed -n '3!p' INPUTFILE
,sed -n '3,5!p' INPUTFILE
,甚至是sed -n '!p' INPUTFILE
)
(注:
sed采用计数器计算,每读取一行,计数器加1,直到最后一行。所以在读到最后一行前,sed是不知道此次输入流中总共有多上行,也不知道最后一行是第几行。"$"符号表示最后一行,它只是一个特殊的标识符号。当sed读取到输入流的尾部时,sed就会为该行打上该标记。没法使用"$"参与数学运算,例如没法使用$-1
表示倒数第二行,由于sed在读到最后一行前,它并不知道这是倒数第二行,此时也还没打"$"标记,所以$-1
是错误的定址表达式。另外一方面,在本译文的最开始就说明了,sed只能经过一次输入流,这意味着已经读取过的行没法再次被读取,因此sed不提供往回取数据的定址表达式,上面的几个定址表达式也确实证实了这一点。事实上,sed中根本就没法使用"-"作减法或取负数,由于语法不支持。
范围匹配的是pattern space中的内容,对于regexp1,regexp2
这样的范围定址天然容易理解,但若是是num1,num2
这样的范围定址,它可能和想象中的匹配方式不同。每读取一行,sed内部的行号计数器都会"+1",因此行号值老是记录在内存中。当进行行号匹配时,其本质是和内存中当前计数器的值进行匹配,若是当前计数器的值在范围内,则能被匹配,不然没法匹配。所以,从hold space回到pattern space的行或者被修改的行甚至是被删除过的行,无论这一行的内容在pattern space中是否存在,只要当前计数器值在范围内,就能匹配。例如多行模式:
echo -e "abc\nfgh" | sed -n 'N;1p'
该命令不输出任何内容。虽然N读取下一行后,pattern space两行中的第一行一直原封不动的放在那,没作任何处理,但"1p"根本就没法匹配这一行,由于当前计数器的值为2。
)。
(注:sed解析、编译和匹配正则表达式的引擎和grep的引擎同样,所以本文基本不作翻译,能够参考我对info grep的译文:grep命令中文手册。)
To know how to use 'sed', people should understand regular expressions ("regexp" for short). A regular expression is a pattern that is matched against a subject string from left to right. Most characters are "ordinary": they stand for themselves in a pattern, and match the corresponding characters in the subject. As a trivial example, the pattern
The quick brown fox
matches a portion of a subject string that is identical to itself. The power of regular expressions comes from the ability to include alternatives and repetitions in the pattern. These are encoded in the pattern by the use of "special characters", which do not stand for themselves but instead are interpreted in some special way. Here is a brief description of regular expression syntax as used in 'sed'.
'CHAR'
A single ordinary character matches itself.
'*'
Matches a sequence of zero or more instances of matches for the preceding regular expression, which must be an ordinary character, a special character preceded by '\', a '.', a grouped regexp (see below), or a bracket expression. As a GNU extension, a postfixed regular expression can also be followed by '*'; for example, 'a**' is equivalent to 'a*'. POSIX 1003.1-2001 says that '*' stands for itself when it appears at the start of a regular expression or subexpression, but many nonGNU implementations do not support this and portable scripts should instead use '\*' in these contexts.
'\+'
As '*', but matches one or more. It is a GNU extension.
'\?'
As '*', but only matches zero or one. It is a GNU extension.
'\{I\}'
As '*', but matches exactly I sequences (I is a decimal integer; for portability, keep it between 0 and 255 inclusive).
'\{I,J\}'
Matches between I and J, inclusive, sequences.
'\{I,\}'
Matches more than or equal to I sequences.
'\(REGEXP\)'
Groups the inner REGEXP as a whole, this is used to:
Apply postfix operators, like '\(abcd\)*'
: this will search for zero or more whole sequences of 'abcd', while 'abcd*'
would search for 'abc' followed by zero or more occurrences of 'd'. Note that support for '\(abcd\)*'
is required by POSIX 1003.1-2001, but many non-GNU implementations do not support it and hence it is not universally portable.
Use back references (see below).
'.'
Matches any character, including newline.
'^'
Matches the null string at beginning of the pattern space, i.e. what appears after the circumflex must appear at the beginning of the pattern space.
In most scripts, pattern space is initialized to the content of each line (*note How 'sed' works: Execution Cycle.). So, it is a useful simplification to think of '^#include' as matching only lines where '#include' is the first thing on line--if there are spaces before, for example, the match fails. This simplification is valid as long as the original content of pattern space is not modified, for example with an 's' command.
'^' acts as a special character only at the beginning of the regular expression or subexpression (that is, after '(' or '|'). Portable scripts should avoid '^' at the beginning of a subexpression, though, as POSIX allows implementations that treat '^' as an ordinary character in that context.
'$'
It is the same as '^', but refers to end of pattern space. '$' also acts as a special character only at the end of the regular expression or subexpression (that is, before ')' or '|'), and its use at the end of a subexpression is not portable.
'[LIST]'
'[^LIST]'
Matches any single character in LIST: for example, '[aeiou]' matches all vowels. A list may include sequences like 'CHAR1-CHAR2', which matches any character between (inclusive) CHAR1 and CHAR2.
A leading '^' reverses the meaning of LIST, so that it matches any single character not in LIST. To include ']' in the list, make it the first character (after the '^' if needed), to include '-' in the list, make it the first or last; to include '^' put it after the first character.
The characters '$', '*', '.', '[', and '\'
are normally not special within LIST. For example, '[\*]'
matches either '\' or '*'
, because the '\' is not special here. However, strings like '[.ch.]', '[=a=]', and '[:space:]' are special within LIST and represent collating symbols, equivalence classes, and character classes, respectively, and '[' is therefore special within LIST when it is followed by '.', '=', or ':'. Also, when not in 'POSIXLY_CORRECT' mode, special escapes like '\n' and '\t' are recognized within LIST. *Note Escapes::.
'REGEXP1\|REGEXP2'
Matches either REGEXP1 or REGEXP2. Use parentheses to use complex alternative regular expressions. The matching process tries each alternative in turn, from left to right, and the first one that succeeds is used. It is a GNU extension.
'REGEXP1REGEXP2'
Matches the concatenation of REGEXP1 and REGEXP2. Concatenation binds more tightly than '|', '^', and '$', but less tightly than the other regular expression operators.
'\DIGIT'
Matches the DIGIT-th '(...)' parenthesized subexpression in the regular expression. This is called a "back reference". Subexpressions are implicity numbered by counting occurrences of '(' left-to-right.
'\n'
匹配换行符。
'\CHAR'
Matches CHAR, where CHAR is one of '$', '*', '.', '[', '\', or '^'
. Note that the only C-like backslash sequences that you can portably assume to be interpreted are '\n' and '\\'
; in particular '\t' is not portable, and matches a 't' under most implementations of 'sed', rather than a tab character.
注意,sed支持的正则表达式是贪婪匹配的。例如,从行左向行右匹配时,若是知足匹配的匹配字符有多个,则会取最长的。(注:例如字符串'abbbc',给定正则表达式"ab*",知足该正则的字符串序列有"a","ab","abb"以及"abbb",因为是贪婪匹配,它会取最长的,即"abbb")
示例:
'abcdef'
匹配字符串"abcdef"。
'a*b'
匹配0或多个a,但后面须要跟上字符b,例如'b'或'aaaaab'。
'a\?b'
匹配"b"或"ab"。
'a\+b\+'
匹配一个或多个a,且后面须要有一个或多个b,因此"ab"是最短的匹配序列,其余的如'aaaab'、'abbbbb'或'aaaaaabbbbbbb'都能被匹配。
'.*'
'.\+'
都匹配全部字符;可是,第一个匹配任何字串(包含空字串),而第二个只匹配包含至少一个字符的字串(空格也是字符)。
(注:即第一种会匹配全部,包括null字符,第二种不能匹配null字串。例如:sed -n '/.*/p'
会匹配空行。sed -n '/.\+/p'
不会匹配空行,这是匹配非空行的一种简便方法。)
'^main.*(.*)'
匹配以"main"开头的行,且该行还包括括号。
'^#'
匹配以"#"开头的行。
'\\$'
匹配以反斜线结尾的行。
'\$'
匹配包含美圆符号的行。
'[a-zA-Z0-9]'
在C字符集环境下,这匹配任意ASCII的字母和数字。
'[^ tab]\+'
(此处tab表明一个制表符)匹配不包含空格和制表符的行。一般用于匹配整个单词。
'^\(.*\)\n\1$'
匹配相邻两行彻底相同的状况。
'.\{9\}A$'
匹配9个任意字符,后面还带一个字母A。
'^.\{15\}A'
匹配以任意15个字符开头但第16个字符是A的行。
若是要掌握sed,下面几个命令几乎是必须掌握的。
'#'
不能跟在定址表达式后。
以"#"开头的行表示注释行。
若是要考虑可移植性,须要注意有些sed可能仅支持一条单行注释,且此时"#"必须是SCRIPT中的第一个字符。
注意:若是SCRIPT中的前两个字符是"#n",则表示强制开启选项"-n",而非注释。若是想要开启一个注释行来注释以字母"n"开头的字符串,那么须要使用大写字母N代替,或者"#"和"n"之间加上一个或多个空白字符。
'q [EXIT-CODE]'
该命令只能在单定址表达式下使用。
表示当即退出sed,而再也不处理后续的命令和输入流。注意,退出sed前,当前pattern space中的内容会被输出,除非使用了"-n"选项。返回一个退出状态码是GNU sed的扩展功能。(注:后文还有一个"Q"命令,和"q"命令做用相同,但退出sed前不输出当前pattern space中的内容。)
'd'
删除模式空间中的内容,并当即进入下一个sed循环(注:即以"break"的方式直接退出当前SCRIPT循环,并读取输入流中的下一行,再执行SCRIPT循环)。
'p'
输出当前模式空间的内容(到标准输出中)。该命令通常只和"-n"选项一块儿使用。
'n'
输出模式空间的内容(除非自动输出功能被禁止),而后读取下一行到模式空间中替换其中的内容。若是输入流中没有下一行供"n"读取(注:即此前已经读取了输入流的最后一行),sed将直接退出,不会执行SCRIPT中后续的命令。
(
注:如下面两个命令为例:其中"i"命令为插入字符串并输出。
[root@xuexi ~]# echo -e "line1\nline2\nline3\nline4" | sed -n 'n;p;i aaa'
line2
aaa
line4
aaa
[root@xuexi ~]# echo -e "line1\nline2\nline3" | sed -n 'n;p;i aaa'
line2
aaa
第二个命令读取了第三行到pattern space后执行n命令,但此时输入流中已经没有下一行供读取,因而直接结束,链后续的"p"命令都没有执行。
)
'{ COMMANDS }'
使用大括号将一系列命令组合成一个命令组。在某些时候特别有用,例如相对某一匹配行或某一范围中的行作屡次处理时。
sed的"s"命令(该命令用于字符串替换)的语法格式为"s/REGEXP/REPLACEMENT/FLAGS"
。"s"命令中的字符"/"能够统一被替换成任意其余单个字符(注:在定址正则表达式中斜杠也能够被替换成其余字符,但须要在第一个被替换字符前加上反斜线转义,例如\%REGEXP%
,而s命令中替换时无需加反斜线)。若是斜线字符"/"(或被替换后的其余字符)须要出如今REGEXP或REPLACEMENT中,必须使用反斜线进行转义。
sed的"s"命令算的上是sed程序中最重要的命令,它有不少不一样的选项。但它的基本概念很简单:"s"命令使用REGEXP匹配pattern space中的内容,若是匹配成功,则匹配成功的那部分字符串被替换为REPLACEMENT。
REPLACEMENT中可使用"\N"
(N是从1到9的整数)进行后向引用,所表明的是REGEXP第N个括号\(...\)
中匹配的内容。另外,REPLACEMENT中能够包含未转义的"&"符号,这表示引用pattern space中被匹配的整个内容(注:是pattern space中的全部匹配,不只仅只是括号的分组匹配)。最后,GNU sed为REPLACEMENT还提供了一些"反斜线加单字母"的特殊字符序列,以下:
'\L'
将REPLACEMENTE转换成小写,直到遇到了"\U"或"\E"。
'\l'
将REPLACEMENTE中下一个字符转换成小写。
'\U'
将REPLACEMENTE转换成大写,直到遇到了"\L"或"\E"。
'\u'
将REPLACEMENT中下一个字符转换成大写。
'\E'
中止"\L"和"\E"开启的大小写转换。
(
注:使用方法以下示例
shell> echo hello | sed 's/e/ \Uyour\Lname /'
h YOURname llo
)
当使用了"g"修饰符时,大小写转换不会从一个正则匹配事件扩展到另外一个正则匹配事件。例如,若是pattern space中的内容为"a-b-",执行下面的命令:
s/\(b\?\)-/x\u\1/g
获得的输出为"axxB"。当替换第一个"-"时,"\u"
只影响"\1"
表明的空值。当替换"b-"时,因为"\1"
表明的是"b",因此字符"b"会被替换为"B",但添加到pattern space中的"x"仍然为小写。
另外一方面,"\l"
和"\u"
会影响REPLACEMENT中的空引用的后一个字符。例如:模式空间内容为"a-b-"
,执行下面的命令:
s/\(b\?\)-/\u\1x/g
将使用"X"(大写)替换"-",使用"Bx"替换"b-"。若是这样的结果不是你想要的结果,能够在此例中的"\1"
后加上"\E"
防止"x"被转换。
若是想要在最后的REPLACEMENT中包含字面符号"\"
、"&"
或换行符,须要在REPLACEMENT中这些字符以前加上反斜线。
sed的"s"命令能够接0或多个以下列出的修饰符(FLAGS):
'g'
使用REPLACEMENT替换全部被REGEXP匹配的内容,而非第一次被匹配到的。
(注:sed任何动做都是在pattern space进行的,字符串替换也如此。不加"g"修饰符时,将只pattern space中第一个被匹配到的内容,加上"g",将替换pattern space中全部被匹配到的内容,所以若是是多行工做模式,第二行或更多行只要能被匹配都会被替换。)
'NUMBER'
为一个整数值N。表示只替换第N个被匹配到的内容。
注意:POSIX标准中没有说明既指定"g"又指定"NUMBER"修饰符时会如何处理,而且当前各类sed的实现也没有标准说法。对于GNU sed而言,定义以下:忽略第NUMBER个匹配前的全部匹配,而后从第NUMBER个匹配开始向后从新匹配并替换全部匹配成功的内容。
'p'
替换动做完成后打印新的模式空间中的内容。
注意:当既指定"p"又指定"e"命令时,它们的先后顺序不一样,会获得两种不一样结果。通常来讲,"ep"
这种顺序获得的结果多是所指望的结果,但另外一种顺序"pe"
对调试颇有用。出于这个缘由,当前版本的GNU sed特意解释了"p"命令在"e"命令前(或后)时,将在"e"命令生效前(或后)输出内容,由于"s"命令的每一个修饰符通常都只展现一次结果。虽然这种行为已经写入文档,但将来可能会改变。
'w FILE-NAME'
该子命令表示将模式空间的内容写入到指定的文件FILE-NAME中。GNU sed支持两个特殊的FILE-NAME:"/dev/stderr"
和"/dev/stdout"
,分别表示写入到标准错误和标准输出中。
'e'
该命令容许经过管道将shell命令的执行结果直接传递到pattern space中。当替换动做完成后,将搜索pattern space,发现的命令会被执行,而且执行结果覆盖到当前pattern space中。它会禁用尾随换行符,而且若是待执行命令中包含了NULL字符,该命令将不被定义为命令,即表示它是普通字符而不是命令因此不执行。这是GNU sed的功能。
(
注:"s"命令的"e"修饰符彷佛只有搜索pattern space并找出其中的命令并执行的功能,没有选项描述中第一句话所述的功能。但"e"命令有该功能,例如:
echo -e "good\nbad" | sed 'e echo haha'
haha
good
haha
bad
不讨论e命令的用法,关于"s"命令的"e"修饰符的用法以下:
文件ttt.txt的内容以下:
[root@xuexi tmp]# cat ttt.txt
ls /tmp
haha
excute a command
将第二行的"haha"替换成一个命令后使用"e"修饰符,那么在替换完成后会查找模式空间,若是找到了能够执行的命令就执行。
[root@xuexi tmp]# sed 's/haha/du -sh \/tmp/ge' ttt.txt
ls /tmp # 注意这一行没有执行
18M /tmp # 说明执行了du -sh /tmp的命令
excute a command
注意到第一行虽然也是能够执行的命令,可是却没有执行,由于"e"是"s"命令的修饰符,须要成功匹配"haha"的行才能符合"e"修饰符。
注意模式空间中的内容是一行一行的,命令将把整行内容做为命令行。例如,若是只将excute替换成du -sh /tmp
,那么模式空间中的内容将是"du -sh /tmp a command",它会对/tmp和当前目录下的"a和"command"目录进行"du -sh"统计,但极可能"a"或"command"目录根本不存在,这时就会报错。
[root@xuexi tmp]# sed 's%excute%du -sh /tmp%ge' ttt.txt
ls /tmp
haha
du: cannot access 'command': No such file or directory # 不存在command目录因此错误
18M /tmp
4.0K a # 当前目录下正好存在a目录,因此统计了信息
而且若是替换后找到的命令不在行首(有前导空白没有影响),将出现错误,由于是将整行做为命令来执行的。
[root@xuexi tmp]# sed 's%command%du -sh /tmp%ge' ttt.txt
ls /tmp
haha
sh: excute: command not found
因此更保险的方法是对整行进行替换。
[root@xuexi tmp]# sed 's%^excute.*%du -sh /tmp%ge' ttt.txt
ls /tmp
haha
18M /tmp
固然,若是想要执行第一行的"ls /tmp"命令也很简单,只需匹配这一行。也能够在此命令上进行命令扩展。以下面将/tmp替换后,模式空间的内容是"ls -ld /tmp;du -sh /tmp",因此会执行这两条命令。
[root@xuexi tmp]# sed 's!/tmp!-ld /tmp;du -sh /tmp!ge' ttt.txt
dr-xr-xr-x. 17 root root 8736768 Oct 25 14:11 /tmp
18M /tmp
haha
excute a command
sed '${p;s/.*/:>filename/e;d} filename'
)
'I'
'i'
这两个修饰符做用相同,表示在REGEXP匹配的时候忽略大小写。这是GNU扩展功能。
'M'
'm'
和前文定址表达式中所述的M修饰符做用一致。见M修饰符。
(注:除了上述明确的修饰符外,还有一个特殊的不算修饰符的符号"\",当它写在"s"命令的REPLACEMENT的每一个行尾时,表示新转义当前命令行的行尾,也就是开启一个新行。例如:
echo 'abcdef' | sed 's/^.*$/\ &\ /'
这表示在abcdef这一行内容先后分别加一个空行,也就是将abcdef这一行嵌入到空行中间。因此可将其理解为换行符"\n",但必须注意,若是转义新行后有内容,则此新行必须再次使用反斜线终止该行,由于它的行尾已经被转义了。例如:
echo 'abcdef' | sed 's/^.*$/\ &\ xyz\ /'
echo 'abcdef' | sed 's/^.*$/&\ /'
其实我的以为使用"\n"方便多了,即容易理解又容易控制。例如上面最后一个命令改成:echo 'abcdef' | sed 's/^.*$/&\n/'
。在后文的好几个例子中都是用了转义行尾的技巧,因此此处提上一提。
)
虽然可能用的不如前面所述的命令频繁,但这些小命令有时候很是有用。
'y/SOURCE-CHARS/DEST-CHARS/'
(y命令中的斜线"/"能够统一被替换成其余单个字符。)
转换pattern space中能被SOURCE-CHARS匹配的字符为DEST-CHARS,且是一一对应地转换。
斜线"/"(或被替换为的其余字符)、反斜线"\"和换行符能够出如今SOURCE-CHARS或DEST-CHARS中,但须要使用反斜线进行转义。(转义后的)SOURCE-CHARS和DEST-CHARS中字符的数量必须彻底相同。
'a\'
'TEXT'
是GNU sed的扩展功能,该命令能够在两个地址格式的定址表达式后使用。
队列化该命令后的文本内容(最后一个反斜线"\"是文本结束符,在输出时该符号会被移除),并在当前SCRIPT循环结束时输出,或从输入流中读取下一行时输出。(注:本质是:只要"有读取下一行"的动做就会触发队列化内容的输出,不止是"a"、还有"i"和"c"以及"r"等,另外,除了sed循环的第一个动做能够读取下一行,命令"N"和"n"都会读取下一行,所以它们也会触发这些队列化内容的输出)
输入的文本内容中若是要出现反斜线字符,须要进行转义,即"\\"
。
做为一个GNU扩展功能,若是命令"a"和换行符之间存在非空白字符"\"序列,则此反斜线会开启新行,而且反斜线后的第一个非空白字符做为下一行的第一个字符。这一样适用于下面的"i"和"c"命令。
(注:命令"a"为尾部追加插入,其动做是将输入文本队列化在内存中,它不会进入模式空间,而是隐含动做自动输出模式空间内容时,在半路追上stdout并将队列化的文本追加到其尾部,并同时输出。下面的"i"和"c"命令所操做都是stdout,都不会进入pattern space。也就是说,这3个命令操做的文本内容不受任何sed其余选项和命令控制。如"-n"选项没法禁止该输入,由于"-n"禁止的是pattern space的自动输出,同时,若是SCRIPT中这3个命令后还有后续的命令序列,则这些命令也没法匹配和操做这些文本内容,例如"p"命令不会输出这些队列化文本,由于它不是pattern space中的内容。
这是sed程序中少见的几个在pattern space外处理数据的动做。具体用法以下两个示例:
[root@xuexi tmp]# cat set2.txt
carrot
cookiee
gold
[root@xuexi tmp]# sed '/carrot/a\iron\nsteel' set2.txt # 换行插入
carrot
iron
steel
cookiee
gold
[root@xuexi tmp]# sed '/carrot/a\iron\ # iron做为第一行,其后跟\继续输入下一行
> steel' set2.txt # 直到遇到结束引号,队列化文本才结束 carrot iron steel cookiee gold
)
'i\'
'TEXT'
是GNU的扩展功能,该命令能够在两个地址格式的定址表达式后使用。
当即输出该命令后的文本(除了最后一行,每一行都以反斜线"\"结尾,但在输出时会自动移除)。
(注:"i"命令同"a"命令同样,只不过是在stdout流的头部加上队列化的文本,并同时输出。它一样不进入pattern space,操做的也是stdout流)
'c\'
'TEXT'
删除从模式空间输出的内容,并在最后一行处(若是没有指定定址选项则每一行)输出"c"命令后队列化文本(除了最后一行,每行都以"\"结尾,输出时会移除)。该命令处理完后会开始新的sed循环,由于模式空间的内容将会被删除。
(注:"c"即change,表示使用队列化文本修改输出流,虽然说是修改,但其实是替换模式空间的输出流并输出。它和"i"、"a"两个命令不同,它"冒充"了输出流输出后就当即进入下一个sed循环,使得SCRIPT中后续的命令不会执行,而"i"、"a"则不会退出循环,而是会继续回到SCRIPT循环中执行后续的命令)
(注:虽然这3个命令用的远不如"s"命令频繁,但我我的认为,理解了这3个命令,就理解了大半sed的工做机制。)
'='
是GNU扩展功能,能够在两个地址格式的定址表达式后使用。
该命令用于输出当前正在处理的输入流中的行号(行号后会加上尾随换行符)。
(注:这是将sed内部保存的行号计数器的值输出,是内存中的内容,所以不进入pattern space,不受"-n"选项影响。)
'l N'
将模式空间中的内容以一种明确的形式打印:非打印字符(和"\"字符)被打印成C语言风格的转义形式;长行被分割后打印,使用"\"字符来表示分割的位置;每一行的结尾被标记上"$"符。
N指定了行分割时指望的行长度(即多少字符换行),长度指定为0表示不换行。若是忽略N,则使用默认长度(默认70)。N参数是GNU sed的扩展功能。
'r FILENAME'
GNU扩展功能,该命令接受两个地址的定址表达式。
读取filename中的内容并按行队列化,而后在当前SCRIPT循环的最后或者读取下一行前插入到output流中。注意,若是filename没法被读取,它将被当成一个空文件而不会产生任何错误提示。
做为GNU sed的扩展功能,支持特殊的filename值:/dev/stdin,表示从标准输入中读取内容。
(注:
"r"命令的功能和"a"命令的做用是彻底同样的,连工做机制都彻底相同,除了"r"命令是从文件中读取队列化文本,而"a"读取的是命令行中提供的。
从"r"命令以后到换行符或sed的引号之间的全部内容都做为"r"的文件参数。所以r命令以后若是还有命令,应当分行写。
"r"命令是一次队列化filename中的全部行,后文还有一个"R"命令是每次队列化filename中的一行,它们都受输出流的影响,只要有输出流就追加到输出流中,并开始下一轮的队列化过程。并不是sed每从输入流中读取一行就队列化一次)
)
'w FILENAME'
将模式空间的内容写入到filename中,做为一个GNU sed扩展,它支持两种特殊的filename值:/dev/stderr和/dev/stdout,分别表示将模式空间的内容写到标准错误和标准输出中。
在输入流的第一行被读入前,filename文件将被建立(或截断,即清空);全部引用相同filename的"w"命令(包括替换命令"s"后的"w"修饰符)不会先关闭filename文件再打开该文件来写入。
(注:
)
'D'
若是pattern space中未包含换行符,则像"d"命令中提到的同样进入下一个sed循环。不然删除pattern space中第一个换行符以前的全部内容,并从新回到SCRIPT的顶端从新对pattern space中剩下的内容进行处理,而不会读取输入流中的下一行。
(注:换句话说,D命令老是删除pattern space中第一个换行符前的内容,若是删除后pattern space中还有内容剩下,则直接回到SCRIPT顶端从新对剩下的这部份内容执行命令,即以"continue"的方式强制进入下一个SCRIPT循环,若是pattern space中没有剩下内容,则直接退出当前SCRIPT循环,并进入下一个sed循环。)
'N'
在当前pattern space中的尾部加上一个换行符,并读取输入流的下一行追加到换行符后面。若是输入流中没有下一行供"N"读取,则直接退出sed循环。
(注:不管是sed循环的第一步、仍是n命令或是N命令,它们读取下一行到pattern space时总会移除行尾换行符。但N命令在读取下一行前在当前pattern space的尾部加上了一个"\n",这使得pattern space中有了两行,若是一个SCRIPT中有屡次N命令还可能有更多行。所以N命令是sed进入多行工做模式的方法。但要注意,虽然称为多行模式,但在pattern space中加入换行符并不是真的换行,在pattern space中还是一行显示的,由于它能被匹配到,且在计算字符数量时会被计入,它仅仅只是一个特殊符号,只不过在输出时会换行显示)
'P'
输出pattern space中第一个换行符"\n"前面的内容。
(注:即输出pattern space中的第一行)
'h'
使用pattern space中的内容替换hold space中的内容。 (注:替换后,pattern space内容仍然保留,不会被清空)
'H'
在hold space的尾部追加一个换行符,并将pattern space中的内容追加到hold space的换行符尾部。
(注:追加后,pattern space内容仍然保留,不会被清空)
'g'
使用hold space中的内容替换pattern space中的内容。
'G'
在当前pattern space的尾部追加一个换行符,并将hold space中的内容追加到pattern space的换行符尾部。
(注:这是另外一个进入多行模式空间的方法。这就有了一个特殊的用法,sed 'G' filename
能够在filename的每一行后添加一个空行,这也是不少人产生误解的地方,认为hold space的初始内容为空行,追加到pattern space就成了空行。其实并不是如此,由于不管是pattern space仍是hold space在初始时都是空的,G命令在pattern space的尾部加上了换行符,并将hold space中的空内容追加到换行符后,使得这成了一个空行。)
'x'
交换pattern space和hold space的内容。
在大多数状况下,使用这些命令意味着你可能可使用像"awk"或"perl"等的工具更好地达到目的。可是偶尔有人会坚持使用'sed',这些命令可能会使得脚本变得很是复杂。
(注:虽然说标签功能不多使用,但有时候确实很是有用,它在sed脚本内部实现了简单的编程结构体)
':LABEL'
不可接在定址表达式后。
设置一个分支标签LABEL供"b"、"t"或"T"(注:"T"命令在后文的GNU sed扩展中说明)命令跳转。除此功能,无任何做用。
(注:冒号和LABEL之间不能有空格,虽然原文中有空格,但这是错误的)
'b LABEL'
无条件跳转到标签LABEL上。若是LABEL参数省略,则跳转到SCRIPT的尾部准备进入下一个sed循环,即跳转到隐含动做auto_print
的前面。
't LABEL'
若是对最近读入的行有"s"命令的替换成功了,则跳转到LABEL标签处。若是LABEL参数省略,则跳转到SCRIPT的尾部准备进入下一个sed循环,即跳转到隐含动做auto_print
的前面。(注:该命令和"T LABEL"相反,"T"是在"s"替换不成功时跳转到指定标签。)
(注:另外,"t"的跳转条件是有"s"命令替换成功,不是最近一个"s"命令替换成功,因此若是有多个"s"命令,即便"t"命令最近的"s"命令不成功,则仍会跳转,直到一个跳转循环内都没有替换成功的才终止跳转。例如:
`echo 'abcdef' | sed ':x;s/haha/yyy/;s/def/haha/;s/yyy/zzz/;tx'`
abczzz
第一轮,被读取的输入行在第一个"s"和第3个"s"命令失败,但第二个成功,因此仍会执行跳转,因而进入第二轮。在第二轮,第一个"s"和第三个"s"替换成功,因此继续跳转,因而进入第三轮。在第三轮中,全部"s"都失败,因此再也不跳转。
)
(注:篇幅缘由,使用一个示例简单解释一下标签和跳转命令的使用。
auto_print
的前面。假设有以下test.txt文件,该文件中空白处都是一个个的空格。目的是多个连续的空格替换成圆圈"○",有几个空格就替换成几个圆圈,但初始时只有一个空格的不进行替换(即第一行的第一个空格和最后一行的最后一个空格不替换)。
[root@xuexi ~]# cat test.txt
sleep sleep
sleep sleep
sleep sleep
sleep sleep
sleep sleep
sleep sleep
sleep sleep
可使用下面简单的命令来实现:
[root@xuexi ~]# sed 's/ /○○/g;s/○ /○○/g' test.txt
sleep○○○○○○○sleep
○○sleep○○○○○○sleep
○○○sleep○○○○○sleep
○○○○sleep○○○○sleep
○○○○○sleep○○○sleep
○○○○○○sleep○○sleep
○○○○○○○sleep sleep
若是使用标签功能,就能够变相地实现sed命令组的循环和条件判断功能。例如使用"b"标签跳转功能来实现,语句以下:
[root@xuexi ~]# sed '
:replace;
s/ /○○/;
/ /b replace;
s/○ /○○/g' test.txt
该语句首先定义了一个标签replace
,在其后是第一个要执行的命令,做用是将两个空格替换成两个圆圈的"s"命令,随后是"b"标签跳转语句,表示若是行中有两个连续的空格,就使用"b"命令跳转到replace
标签处,因而再次执行"s"命令,替换结束后再次回到/ /b replace
命令,因而这样就能够对每一输入行实现循环替换动做,直到最后行中没有连续空格。但此时可能还会有一个多余的空格跟在圆圈后面,因而就能知足s/○ /○○/g
命令的替换条件。
一样,还可使用"t"标签完成上述要求。
[root@xuexi ~]# sed '
:replace;
s/ /○○/;
t replace;
s/○ /○○/g' test.txt sleep○○○○○○○sleep ○○sleep○○○○○○sleep ○○○sleep○○○○○sleep ○○○○sleep○○○○sleep ○○○○○sleep○○○sleep ○○○○○○sleep○○sleep ○○○○○○○sleep sleep
其实"t"标签更可能用于实现像shell中case的条件判断功能。例如:
sed '{ s/abc/ABC/; t; s/opq/OPQ/; t; s/xyz/XYZ/}' filename
这表示若是能替换什么成功,就跳转到尾部结束,是多选一的状况。
)
这些命令是GNU sed特有的命令,所以必须谨慎使用它们,而且sed脚本没有可移植性的要求。
'e [COMMAND]'
该命令容许将shell命令行的输出结果插入到pattern space中。不给定参数COMMAND时,"e"命令将搜索pattern space并执行发现的命令,并将命令的执行结果替换到pattern space中(注:相似于shell下的命令替换功能)。
若是指定了"COMMAND"参数,"e"命令将解析它并认为它是一个shell命令,并在(子)shell环境下执行后将结果发送到输出流中。
不管是否指定了COMMAND参数,若是待执行命令中包含了空字符,则都不被认为是一个命令。
注意,不像"r"命令,"e"命令的结果是当即输出的;而"r"命令须要延迟到当前SCRIPT循环结束时才会输出。
'F'
输出当前处理的输入流的文件名(输入完后会换行)。
'L N'
这是一个失败的GNU扩展功能,无视。
'Q [EXIT-CODE]'
该命令只能在单个定址表达式下使用。
该命令和命令"q"相同,除了它不会输出pattern space中的内容(注:"q"命令在退出前会输出当前pattern space中的内容)。一样,像"q"那样能够提供一个退出状态码。
该命令很是有用,由于要完成这个显而易见的简单功能的惟一替代方法是使用'-n'选项(这可能会使脚本没必要要地复杂化)或使用如下代码片断,但这会浪费时间读取整个文件且没有任何效果可见:
(注:即便使用了"-n"选项,性能也不如"q"和"Q",由于它仍会读完整个输入流,而"q | Q"是当即退出sed程序。)
:eat
$d Quit silently on the last line
N Read another line, silently
g Overwrite pattern space each time to save memory
b eat
'R FILENAME'
每次读取FILENAME中的一行并队列化在内存中,其他的像"r"选项同样,在每一个SCRIPT循环结束时追上stdout流并追加在其尾部。若是FILENAME无可读取或到达了文件尾部,将不会给出任何报错信息。
和"r"命令同样,它也支持特殊的FILENAME值:/dev/stdin,表示从标准输入中读取数据。
(注:"R"命令是每次队列化filename中的一行,而"r"命令是一次队列化filename中的全部行。它们都受输出流的影响,有一次输出就追加到输出流中,并开始下一轮的队列化过程。并不是sed每从输入流中读取一行就队列化一次)
'T LABEL'
和"t LABEL"相反,该命令是仅当"s"命令未完成替换时跳转到标签LABEL。若是LABEL参数省略,则跳转到SCRIPT的尾部准备进入下一个sed循环,即跳转到隐含动做auto_print
的前面。
'v VERSION'
该命令除了让sed程序在不支持GNU sed功能的时候失败不作任何事。此外,能够指定一个版本号,表示你的sed脚本必需要高于此版本的sed程序才能运行,例如指定"4.0.5"。默认指定的版本号为"4.0",由于这是第一个支持该命令的版本。
'W FILENAME'
将pattern space中第一个换行符以前的内容写入到filename中。除此以外,和"w"命令彻底相同。
'z'
该命令用于清空pattern space。能够认为它等价于"s/.*//"
,但却效率更高,且能够在输入流中存在无效多字节序列(例如UTF-8时,一个字符占用多个字节)的状况下工做。POSIX要求这样的序列没法使用"."进行匹配,所以没有任何方法既能够清空sed的buffer空间,又能保证sed脚本的可移植性。
直到目前位置,咱们只遇到了"\^"
格式的转义,它告诉sed不要将脱字符"^"解释为特殊元字符,而是解释为一个普通的字面符号。再例如,"\*"
匹配的是单个*
字符,而不是匹配0或多个反斜线字符。
本小节介绍另外一种类型的转义。以下:
'\a'
匹配一个BEL字符,即一个"蜂鸣警告"(ASCII 7)。
'\f'
匹配一个分页符(ASCII 12)。
'\n'
匹配一个换行符(ASCII 10)。
'\r'
匹配一个回车符(ASCII 13)。
'\t'
匹配一个水平制表符(ASCII 9)。
'\v'
匹配一个垂直制表符(ASCII 11)。
'\cX'
Produces or matches 'CONTROL-X', where X is any character. The precise effect of '\cX' is as follows: if X is a lower case letter, it is converted to upper case. Then bit 6 of the character (hex 40) is inverted. Thus '\cz' becomes hex 1A, but '\c{' becomes hex 3B, while '\c;' becomes hex 7B.
'\dXXX'
匹配十进制ASCII值为XXX的字符。
'\oXXX'
匹配八进制ASCII值为XXX的字符。
'\xXX'
匹配十六进制ASCII值为XX的字符。
'\b'
(回退删除键)没法实现,由于"\b"
用于匹配单词的边界。
如下转义序列匹配特殊的字符类,且只在正则表达式中有效:
'\w'
匹配任意单词组成字符。单词的组成成分包括:字母、数字和下划线。其余任意字符都不是单词的一部分。
'\W'
匹配人非单词字符。即匹配除了字母、数字和下划线的任意字符。
'\b'
匹配单词的边界位置。
'\B'
匹配非单词的边界位置。
"\`"
此为sed独有正则表达式。匹配pattern space中的开头。在多行模式下,这和"^"
是不一样的。
"\'"
此为sed独有正则表达式。匹配pattern space中的尾部。在多行模式时,这和"$"
是不一样的。
(注:关于最后两个正则表达式,它们是sed才可使用的。在多行模式下,它们和"^"、"$"的区别,见M修饰符。)
这里有一些sed示例脚本,能够引导你成为大师级的sed使用者。
(注:说实话,下面的17个示例看的我怀疑人生,真的没想到sed功能强大如斯,但也难的想死,这绝对是骨灰级玩家才能玩的)
一些吸引人的示例:
Centering lines
Increment a number
Rename files to lower case
Print bash environment
Reverse chars of lines
模仿一些标准的工具:
tac : Reverse lines of files
cat -n : Numbering lines
cat -b : Numbering non-blank lines
wc -c : Counting chars
wc -w : Counting words
wc -l : Counting lines
head : Printing the first lines
tail : Printing the last lines
uniq : Make duplicate lines unique
uniq -d: Print duplicated lines of input
uniq -u: Remove all duplicated lines
cat -s : Squeezing blank lines
该脚本将全部行都划分中心线,每行最多80个字符,中心线的位置指定为第40个字符处。若是要改变行宽度,则下面的'\{...\}'
中的数字必须更改,同时须要在脚本的第一段中添加或删除适当个数的空格。
注意此处是如何使用buffer空间的命令来分隔两部分的,这是一个通用技巧。
(注:若是行的内容本来就超过81个字符,则这些行的行尾会被截断)
#!/usr/bin/sed -f
# Put 80 spaces in the buffer
1 {
x
s/^$/ /
s/^.*$/&&&&&&&&/
x
}
# del leading and trailing spaces
y/tab/ / s/^ *//
s/ *$//
# add a newline and 80 spaces to end of line
G
# keep first 81 chars (80 + a newline)
s/^\(.\{81\}\).*$/\1/
# \2 matches half of the spaces, which are moved to the beginning
s/^\(.*\)\n\(.*\)\2/\2\1/
(注:今后脚本可知,虽在pattern space中加上了换行符,但其实不是真的换行,它在pattern space中还是一行,只不过在输出时换行符使结果换了行。所以,N命令进入多行模式也是同样的。同时,换行符是一个字符,能被匹配,也能被替换掉)
该脚本是sed中少数几个演示了如何作算术计算的示例。这确实颇有可能,但只能手动实现。
为了增加一个数值,只须要为该数的末尾加上1,并替换原末尾便可。但有一种例外:当末尾数位9时,其前面一位也必须增加,若是这仍是9,则还需增加,直到无需进位时才结束。
Bruno Haible提供的解决方案很是聪明,由于它只使用了一个buffer空间。它是这样工做的:将末尾9替换成下划线,而后使用多个"s"命令增加最后一个数字(下划线前面的数字也属于最后一个数字),最后将全部的下划线替换为0。
(注:下面的td
和tn
是t d
和t n
的简写。)
#!/usr/bin/sed -f
/[^0-9]/ d
# replace all trailing 9s by _ (any other character except digits, could
# be used)
:d
s/9\(_*\)$/_\1/
td
# incr last digit only. The first line adds a most-significant
# digit of 1 if we have to add a digit.
s/^\(_*\)$/1\1/; tn
s/8\(_*\)$/9\1/; tn
s/7\(_*\)$/8\1/; tn
s/6\(_*\)$/7\1/; tn
s/5\(_*\)$/6\1/; tn
s/4\(_*\)$/5\1/; tn
s/3\(_*\)$/4\1/; tn
s/2\(_*\)$/3\1/; tn
s/1\(_*\)$/2\1/; tn
s/0\(_*\)$/1\1/; tn
:n
y/_/0/
(注:例如为该sed脚本传递一个个位数8,因为该数据是以8结尾的,所以将一直执行到s/8\(_*\)$/9\1/;tn
将8增加为9,并跳转到标签n,标签n后的命令不用对其处理,因此直接输出9。假如传递的数字是3999,则:d;s/9\(_*\)$/_\1/;td
将一直循环,首先替换成"399_"
,再循环一次替换成"39__"
,最后一次循环替换成"3___"
,因为此时结尾没9了,且是以3结尾,因而继续向下直到s/3\(_*\)$/4\1/; tn
,它会将"3___"
替换成"4___"
,并跳转到标签n,y/_/0/
将把下划线替换成0获得4000,最后输出结果为4000。
这是一个很是奇怪的sed应用。转换文件名文本,并转换其为shell命令,而后将它们提供给shell。
该应用的主体是其中的sed脚本部分,它会从新将名称的小写字母映射为大写,甚至还检查从新映射后的名称是否和原始名称相同。注意脚本是如何使用shell变量和正确的引号进行参数化的。
#! /bin/sh
# rename files to lower/upper case...
#
# usage:
# move-to-lower *
# move-to-upper *
# or
# move-to-lower -R .
# move-to-upper -R .
#
help()
{
cat << eof
Usage: $0 [-n] [-R] [-h] files...
-n do nothing, only see what would be done
-R recursive (use find)
-h this message
files files to remap to lower case
Examples:
$0 -n * (see if everything is ok, then...)
$0 *
$0 -R .
eof
}
apply_cmd='sh'
finder='echo "$@" | tr " " "\n"'
files_only=
while :
do
case "$1" in
-n) apply_cmd='cat' ;;
-R) finder='find "$@" -type f';;
-h) help ; exit 1 ;;
*) break ;;
esac
shift
done
if [ -z "$1" ]; then
echo Usage: $0 [-h] [-n] [-r] files...
exit 1
fi
LOWER='abcdefghijklmnopqrstuvwxyz'
UPPER='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
case `basename $0` in
*upper*) TO=$UPPER; FROM=$LOWER ;;
*) FROM=$UPPER; TO=$LOWER ;;
esac
eval $finder | sed -n ' # remove all trailing slashes s/\/*$// # add ./ if there is no path, only a filename /\//! s/^/.\// # save path+filename h # remove path s/.*\/// # do conversion only on filename
# (注:注意这里是如何引用变量的。本质是将其还原为shell的token,因此遇到变量,就在其前和后使用引号将其暴露给shell) y/'$FROM'/'$TO'/ # now line contains original path+file, while # hold space contains the new filename x # add converted file name to line, which now contains # path/file-name\nconverted-file-name G # check if converted file name is equal to original file name, # if it is, do not print anything /^.*\/\(.*\)\n\1/b # escape special characters for the shell s/["$'\\]/\\&/g
# now, transform path/fromfile\n, into
# mv path/fromfile path/tofile and print it
s/^\(.*\/\)\(.*\)\n\(.*\)$/mv "\1\2" "\1\3"/p
' | $apply_cmd
该脚本去除了'set'命令中的shell function的定义。
#!/bin/sh
set | sed -n '
:x
# if no occurrence of "=()" print and load next line
/=()/! { p; b; }
/ () $/! { p; b; }
# possible start of functions section
# save the line in case this is a var like FOO="() "
h
# if the next line has a brace, we quit because
# nothing comes after functions
n
/^{/ q
# print the old line
x; p
# work on the new line now
x; bx
'
该脚本可用反转行中字符的位置,它使用了一次性移动两个字符的技术,所以它的速度比直观看上去的更快。
注意标签订义语句前面的"tx"命令,它用来重置"t"标签跳转的条件,省得被第一个"s"命令影响而跳转。
#!/usr/bin/sed -f
/../! b
# Reverse a line. Begin embedding the line between two newlines
# (注:下面的"s"命令使用了转义行尾的技巧,其实和"sed /^.*$/\n&\n/"是等价的)
s/^.*$/\
&\
/
# Move first character at the end. The regexp matches until
# there are zero or one characters between the markers
tx
:x
s/\(\n.\)\(.*\)\(.\n\)/\3\2\1/
tx
# Remove the newline markers
s/\n//g
(注:例如echo abcde | sed -f file.sed
,传入的是"abcde",首先判断它不是单字符,不然将跳转到尾部,而后将"abcde"嵌入到两个相同的特殊字符中,此处采用的是嵌入到两空行中,为了解释方便,假设这里的特殊字符为"#"符号,因而获得"#abcde#",随后一个"t x"命令用于重置跳转条件,避免第一个s命令的成功状态影响跳转条件。再执行第二个s命令,该命令将两个"#"先后的字符反转,第一轮循环获得"e#dcb#a",随后跳转再次替换,获得"ed#c#ba",再次跳转,但这一次替换不成功。最后将全部的"#"删除,即获得最终结果"edcba")
这是一个没什么用但却颇有意思的脚本,是模仿unix命令。就像tac
同样工做。
注意,非GNU sed执行该脚本时很容易内存溢出。
#!/usr/bin/sed -nf
# reverse all lines of input, i.e. first line became last, ...
# from the second line, the buffer (which contains all previous lines)
# is *appended* to current line, so, the order will be reversed
1! G
# on the last line we're done -- print everything
$ p
# store everything on the buffer again
h
该脚本和cat -n
的功能同样。固然,这个脚本没什么用,两个缘由:第一,有人使用C语言实现了该功能,第二,下面这个shell脚本能够实现相同的目的却更快。
#! /bin/sh
sed -e "=" $@ | sed -e ' s/^/ / N s/^ *\(......\)\n/\1 / '
它首先使用sed来输出行号,而后经过"N"两行一分组并进行调整。固然,这个脚本并无下面这个脚本的引导意义多。
这个算法同时使用了两个buffer空间,使得每一行能够尽快被打印出来而后丢弃。而行号被分离,使得数字改变后能够放入一个buffer空间,而没有改变的数字在另外一个buffer空间;改变的数字使用一个"y"命令来修改。因而行号被存储在hold space,在下一个迭代中被使用上。
#!/usr/bin/sed -nf
# Prime the pump on the first line
x
/^$/ s/^.*$/1/
# Add the correct line number before the pattern
G
h
# Format it and print it
s/^/ /
s/^ *\(......\)\n/\1 /p
# Get the line number from hold space; add a zero
# if we're going to add a digit on the next line
g
s/\n.*$//
/^9*$/ s/^/0/
# separate changing/unchanged digits with an x
s/.9*$/x&/
# keep changing digits in hold space
h
s/^.*x//
y/0123456789/1234567890/
x
# keep unchanged digits in pattern space
s/x.*$//
# compose the new number, remove the newline implicitly added by G
G
s/\n//
h
模范的是"cat -b",它不会计算空行的行号。
在此脚本中和上一个脚本相同的部分没有给出注释,于此处可知,对于sed脚原本说,给注释是多么重要的事。
#!/usr/bin/sed -nf
/^$/ {
p
b
}
# Same as cat -n from now
x
/^$/ s/^.*$/1/
G
h
s/^/ /
s/^ *\(......\)\n/\1 /p
x
s/\n.*$//
/^9*$/ s/^/0/
s/.9*$/x&/
h
s/^.*x//
y/0123456789/1234567890/
x
s/x.*$//
G
s/\n//
h
该脚本展现了sed另外一种作数学计算的方式。此处咱们必须能够增大到一个极大的数值,所以要成功实现这一点可能不那么容易。
解决的方法是将数字映射为字母,这是sed的一种算盘功能实现。"a"表示个位数,"b"表示十位数,以此类推。如前所见,咱们仍然在hold space中保存总数。
在最后一行上,咱们将算盘上的值转换为十进制数字。为了能适应多种状况,这里使用了循环而不是一大堆的"s"命令:首先转换个位数,而后从数值中移除字母"a",而后滚动字母使得各个字母都转换成对应的数值。
#!/usr/bin/sed -nf
# Add n+1 a's to hold space (+1 is for the newline)
s/./a/g
H
x
s/\n/a/
# Do the carry. The t's and b's are not necessary,
# but they do speed up the thing
t a
: a; s/aaaaaaaaaa/b/g; t b; b done
: b; s/bbbbbbbbbb/c/g; t c; b done
: c; s/cccccccccc/d/g; t d; b done
: d; s/dddddddddd/e/g; t e; b done
: e; s/eeeeeeeeee/f/g; t f; b done
: f; s/ffffffffff/g/g; t g; b done
: g; s/gggggggggg/h/g; t h; b done
: h; s/hhhhhhhhhh//g
: done
$! {
h
b
}
# On the last line, convert back to decimal
: loop
/a/! s/[b-h]*/&0/
s/aaaaaaaaa/9/
s/aaaaaaaa/8/
s/aaaaaaa/7/
s/aaaaaa/6/
s/aaaaa/5/
s/aaaa/4/
s/aaa/3/
s/aa/2/
s/a/1/
: next
y/bcdefgh/abcdefg/
/[a-h]/ b loop
p
该脚本和前一个差很少,每一个单词都转换成一个单独的字母"a"(上一个脚本中是每个字母转换成一个字母"a")。
有趣的是,"wc"程序对"wc -c"计算字符时作了优化,使得计算单词的速度要比计算字符的速度慢不少。与之相反,这个脚本的瓶颈在于算术,所以单词计算的速度要快的多(由于只需维护少许的数值计算)
此脚本中和上一个脚本的共同部分没有给注释,这再次说明了sed脚本中注释的重要性。
#!/usr/bin/sed -nf
# Convert words to a's
s/[ tab][ tab]*/ /g
s/^/ /
s/ [^ ][^ ]*/a /g
s/ //g
# Append them to hold space
H
x
s/\n//
# From here on it is the same as in wc -c.
/aaaaaaaaaa/! bx; s/aaaaaaaaaa/b/g
/bbbbbbbbbb/! bx; s/bbbbbbbbbb/c/g
/cccccccccc/! bx; s/cccccccccc/d/g
/dddddddddd/! bx; s/dddddddddd/e/g
/eeeeeeeeee/! bx; s/eeeeeeeeee/f/g
/ffffffffff/! bx; s/ffffffffff/g/g
/gggggggggg/! bx; s/gggggggggg/h/g
s/hhhhhhhhhh//g
:x
$! { h; b; }
:y
/a/! s/[b-h]*/&0/
s/aaaaaaaaa/9/
s/aaaaaaaa/8/
s/aaaaaaa/7/
s/aaaaaa/6/
s/aaaaa/5/
s/aaaa/4/
s/aaa/3/
s/aa/2/
s/a/1/
y/bcdefgh/abcdefg/
/[a-h]/ by
p
就像wc -l
同样,统计行数。
#!/usr/bin/sed -nf
$=
该脚本是sed脚本中最有用又最简单的。它显示了输入流的前10行。使用"q"的做用是当即退出sed,再也不读取额外的行浪费时间和资源。
#!/usr/bin/sed -f
10q
输出倒数N行比输出顺数前N行要复杂的多,但确实颇有用。N值是下面脚本中1,10 !s/[^\n]*\n//
的数值10对应值,此处表示输出倒数10行。
该脚本相似于tac
脚本,都将每次的结果保留在hold space中最后输出。
#!/usr/bin/sed -nf
1! {; H; g; }
1,10 !s/[^\n]*\n//
$p
h
关键点,该脚本维持了一个10行的窗口,在向其中添加一行时滑动该窗口并删除最旧的一行(第二个s命令有点相似于"D"命令,但却不用从新进入SCRIPT循环)
在写高级或复杂sed脚本时,"窗口滑动"的技术做用很是大,由于像"P"这样的命令要实现相同的目的须要手动作不少额外的工做。
为了介绍这种技术,在剩下的几个示例中,均使用了"N"、"P"和"D"命令充分演示了该技术。此处是一个"窗口滑动"技术对"tail"命令的实现。
这个脚本看上去更复杂,但实际上工做方式和上一个脚本是同样的:当踢掉了合理的行后,再也不使用hold space来保存内部行的状态,而是使用"N"和"D"命令来滑动pattern space:
#!/usr/bin/sed -f
1h
2,10 {; H; g; }
$q
1,9d
N
D
注意在读取了输入流的前10行后,其中的第一、二、4行的命令就失效了。以后,全部的工做是:最后一行时推出,追加下一行到pattern space中并移除其内第一行。
这个示例充分展示了"N"、"D"和"P"命令的艺术所在,这多是成为大师路上最难的几个命令。
#!/usr/bin/sed -f
h
:b
# On the last line, print and exit
$b
N
/^\(.*\)\n\1$/ {
# The two lines are identical. Undo the effect of the N command.
g
bb
}
# If the 'N' command had added the last line, print and exit
$b
# The lines are different; print the first and go
# back working on the second.
P
D
正如所见,咱们使用"P"和"D"维护了一个2行的窗口空间。这种窗口(滑动)技术在高级sed脚本中常常会使用。
(注:"N"、"P"和"D"是sed中绝佳组合,一般为这3个命令关联不一样的定址表达式以及感叹号"!"时,获得的结果变幻无穷。一方面"N"和"D"能够"滑动窗口",另外一方面,"P"和"D"能够输出窗口的第一行并滑动,再配合"N"或其它进入多行模式的方法(如"G",或"s"命令添加了换行符"\n"),使得窗口的大小能够一直维持下去。一般这3个命令同时使用时,它们的相对先后顺序是"NPD"。另外,"D"比较特殊,它会从新进入SCRIPT循环,使得窗口中的内容只保留符合条件的行,所以滑动窗口变得更加"动态",要使用s命令实现这种动态窗口滑动,只能借助"t"标签跳转来实现循环)
该脚本只打印重复行,就像"uniq -d"同样。
#!/usr/bin/sed -nf
$b
N
/^\(.*\)\n\1$/ {
# Print the first of the duplicated lines
s/.*\n//
p
# Loop until we get a different line
:b
$b
N
/^\(.*\)\n\1$/ {
s/.*\n//
bb
}
}
# The last line cannot be followed by duplicates
$b
# Found a different one. Leave it alone in the pattern space
# and go back to the top, hunting its duplicates
D
该脚本移除全部重复行,就像"uniq -u"同样。
#!/usr/bin/sed -f
# Search for a duplicate line --- until that, print what you find.
$b
N
/^\(.*\)\n\1$/ ! {
P
D
}
:c
# Got two equal lines in pattern space. At the
# end of the file we simply exit
$d
# Else, we keep reading lines with 'N' until we
# find a different one
s/.*\n//
N
/^\(.*\)\n\1$/ {
bc
}
# Remove the last instance of the duplicate line
# and go back to the top
D
做为最后一个示例,这里给出了3个脚本,用于增长复杂度和速度,这可使用"cat -s"来实现一样的功能:压缩空白行。
第一个脚本的实现方式是保留第一个空行,若是发现后续还有连续空行,则直接跳过。
#!/usr/bin/sed -f
# on empty lines, join with next
# Note there is a star in the regexp
:x
/^\n*$/ {
N
bx
}
# now, squeeze all '\n', this can be also done by:
# s/^\(\n\)*/\1/
s/\n*/\ /
下面这个脚本要更复杂一些,它会移除全部的第一个空行,并保留最后一个空行。
#!/usr/bin/sed -f
# delete all leading empty lines
1,/^./{
/./!d
}
# on an empty line we remove it and all the following
# empty lines, but one
:x
/./!{
N
s/^\n$//
tx
}
下面这个脚本会移除前导和尾随空行,速度最快。注意,"n"和"b"会完全完成整个循环,而不会依赖于sed自动读取下一行。
#!/usr/bin/sed -nf
# delete all (leading) blanks
/./!d
# get here: so there is a non empty
:x
# print it
p
# get next
n
# got chars? print it again, etc...
/./bx
# no, don't have chars: got an empty line
:z
# get next, if last line we finish here so no trailing
# empty lines are written
n
# also empty? then ignore it, and get next... this will
# remove ALL empty lines
/./!bz
# all empty lines were deleted/ignored, but we have a non empty. As
# what we want to do is to squeeze, insert a blank line artificially
i\
bx
对于那些想写具备可移植性的sed脚本,须要注意有些sed程序有众所周知的buffer大小限制,要求不能超过4000字节,在POSIX标准中明确说明了要不小于8192字节。在GNU sed则没有这些限制。
然而,在匹配的时候是使用递归处理子模式和无限重复。这意味着可用的栈空间可能会限制那些能够经过某些pattern处理的缓冲区的大小。
除了某些关于sed的书籍(专门研究或做为某个章节讨论的shell编程)以外,能够从"sed-users"邮件列表的FAQ中(包含一些sed书籍的推荐和建议)获取更多关于sed的信息:
http://sed.sourceforge.net/sedfaq.html
此外,还有sed的新手教程和其余一些sed相关资源:
http://www.student.northpark.edu/pemente/sed/index.htm
"sed-user"邮件列表由Sven Guckes负责维护,能够访问http://groups.yahoo.com来订阅。
如要报告bug,邮送至bug-sed@gnu.org,请同时包含在邮件的body中包含sed的版本号,即"sed --version"的输出结果。
请不要像下面同样报告bug:
while building frobme-1.3.4
$ configure
error--> sed: file sedscr line 1: Unknown option to 's'
如下是一些容易被报告的bug,但实际上却不是bug:(注:也就是容易让人疑惑的点,对于深刻理解sed帮助很大)
大多数版本的sed在"N"命令处理输入流的最后一行时会直接退出而不打印任何东西。GNU sed会输出pattern space的内容,除非使用了"-n"选项。这是GNU sed特地的。
例如,sed N foo bar
将依赖于"foo"是偶数行仍是奇数行。或者,当想在某个匹配行后读取后续的几行时,传统的sed可能只能写成这样:
/foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N }
来替代:
/foo/{ N;N;N;N;N;N;N;N;N; }
不管什么时候,最简单的工做方式是使用$d;N
,或者设置"POSIXLY_CORRECT"变量为一个非空值,来解决上述传统的依赖行为。
sed使用的是POSIX的基础正则表达式语法,根据POSIX标准,有些转义序列没有预约义,在sed中须要注意的包括:
\|、\+、\?、\<、\>、\b、\B、\w、\W、\'和\`
因为全部的GNU程序都是用POSIX的基础正则表达式,sed会将这些转义符解析为特殊的元字符,所以"x+"会匹配一个或多个"x","abc|def"会匹配"abc"或"def"。
这些语法在用其余版本的sed运行时可能会出现问题,特别是某些版本的sed程序会将"|"和"+"解析为字面符号的"|"和"+"。所以在某些版本的sed上运行须要参考其所支持的正则表达式语法作相应修改。
再者说,某些脚本中"s|abc|def||g"来移除"abc"或"def"字符串,但这只在sed 4.0.x版以前能正常工做,在新版的sed中会将其解析为移除"abc|def"字符串。这在POSIX中一样没有定义对应的标准。
简单地说,"sed -i"将删除只读文件中的内容。通俗地说,"-i"选项会破坏受保护的只读文件。这不是bug,而是Unix文件系统工做方式引发的结果。
普通文件的权限表示的是能够对该文件中的数据作什么样的操做,可是目录的权限表示的是能够对目录中的文件作什么样的操做。"sed -i"毫不会为了写入而再次打开该文件。取而代之的是,它会先写入一个临时文件,最后将其重命名为原文件名:删除或重命名文件的权限是目录权限控制的,所以该操做依赖的是目录的权限,而非文件自己的权限。一样的缘由,"sed -i"不容许修改一个可读但其目录只读的普通文件。
根本就没有0行。0行的概念只有一种状况下使用0,/REGEXP/
,它和1,/REGEXP/
只有一点不一样:若是REGEXP能匹配上第一行,则前者的结果是只有第一行,然后者会继续向下匹配直到能匹配REGEXP,由于范围地址必需要跨越至少两行,除非直到最后一行都没有匹配上。
这是字符集环境设置的问题。POSIX强制'[a-z]'这样的字符列表采用当前系统当前字符集的排序规则进行排序,C字符集环境下,它表明的是小写字母序列,其余字符集环境下,可能表明的是小写和大写字母序列,这依赖于字符集。
为了解决这个问题,能够设置LC_COLLATE
和LC_CTYPE
环境变量为C。
会发生这种状况,多是由于你的输入流中包含了多字节序列(如UTF-8).POSIX强制这样的序列字符没法被"."匹配,所以s/.*//
将不会如你所愿那样清空pattern space。事实上,在绝大多数多字节序列环境下,没有任何办法脚本的中途清空pattern space。出于这个缘由,GNU sed提供了一个"z"命令,它将"\0"做为输入流的行分隔符。
为了解决这些问题,能够设置LC_COLLATE
和LC_CTYPE
环境变量为C。