sed学习笔记及手册翻译¶
sed手册本身不太适合初学者,我在看的过程中加了一些自己的注释和一些例子,帮助理解,如有偏差或错误,请指出,多谢。
Contents
sed的工作原理¶
sed维护两个缓冲区,pattern space和hold space,命令开始执行之前都为空。
pattern space缓冲区用于临时保存每次读取的一行的内容,大部分的匹配和替换等等操作都是针对pattern space中的内容进行的,因此不会对输入文件有任何影响;而hold space则作为后备缓冲区使用。
sed针对输入中的每一行都会执行一个系列命令:首先,从标准输入流读取一行,移除换行符,然后存入pattern space中,接着执行指定的命令。每个命令都有一个可选的地址(也可能是一个正则表达式匹配),这个地址作为一个执行前的测试,指定了需要对那些行进行操作。当前行只有匹配的情况下才会执行命令。
当指定所有的命令都执行完了之后,sed默认会将pattern space中的内容打印到标准输出中,如果指定了-n选项,则会禁止这种默认的打印行为。如果读取本行时移除了行尾的换行符,则在打印了pattern space中的内容后会再打印一个换行符。这样针对本行的操作就完成了,然后sed会读取下一行的内容,再次执行相同的操作。
除非指定了一些特殊的命令(例如D命令),否则pattern space中的内容会在处理完一行之后删除,但hold space中的内容在处理完每一行时不会被删除。
例如:在匹配式样regex的行之前插入一空行,
sed '/regex/{x;p;x;}'
x
Exchange the contents of the hold and pattern spaces.
p
Print out the pattern space (to the standard output). This command is usually only used in conjunction with the `-n' command-line option.
令执行过程如下:先读入一行,删除换行符,然后将其保存到pattern space中,然后用/regex/正则表达式匹配pattern space中的内容,如果匹配的话则执行后续的命令;如果不匹配则执行默认的打印pattern space中内容的操作,然后读取下一行。
如果匹配的话,第一个x命令将pattern space和hold space中的内容交换,由于此时hold space内容为空,实际的效果就是将pattern space的内容移动到hold space中,并且清空pattern space,然后用p命令打印pattern space的内容,由于此时pattern space为空,所以会打印一个空行,接着第二个x命令会又一次交换pattern space和holdspace的内容,实际效果是原先读入的那一行又会从hold space移动到patternspace中,最后打印此行内容。所以以上命令的结果就是在每一个匹配/regex/的行之前插入一个空行。
理解上述命令的关键在于当pattern space为空时,p命令会打印一个空行,而不是一个空字符串。这涉及到sed的工作原理:sed会在读入一行后,去掉行尾的换行符,然后将其内容存入pattern space中,打印时会在最后加一个换行符。所以当pattern space为空时,p命令会先打印一个空字符串,然后再打印一个换行符。
sed中如何选择特定的行¶
sed中有一些指定行范围的方法,用行号指定和用正则表达式指定,以及两者结合起来指定。里面涉及到行号匹配的时候,如果指定了-i或者-s选项的话则是相对于当前文件。否则是相对于整个输入流。
可以使用以下方法来指定行或者行范围:
N
匹配输入中第N行。
FIRST~STEP
从第FIRST行开始,每隔STEP行选择一行。最终选择行的行号等于FIRST+(N*STEP)。1~1选择所有行(在这里没什么意义),1~2选择奇数行,2~2选择偶数行等等。
$
匹配最后一行。
/REGEXP/
选择所有匹配这个正则表达式的行。
%REGEXP%
同样是正则表达式匹配,上面的%可以换成任何字符,主要用于匹配的内容包含大量需要转义的字符的情况下,例如匹配路径时。
/REGEXP/I, %REGEXP%I
同上,忽略大小写的正则表达式匹配。
/REGEXP/M, %REGEXP%M
同上,多行匹配。M表示multi-line。不使用M时,^和$只能匹配pattern space的开始和结尾的位置,如果使用了M修饰符,且pattern space中间有换行符的话,^还会匹配换行符后下一行开头的位置,$还会匹配换行符和它之前的字符中间的位置。
使用这个修饰符要注意,它不会改变sed每次读取一行的默认行为。正因为sed的默认操作行为,一般情况下pattern space中没有换行符。只有在pattern space中间有换行符的情况下M修饰符才会有效果,可以通过N命令来达到这个目的。
例:模式匹配时M修饰符的作用
~/pro/3.System_Config/tools/sed$ echo -e "a\nb\nc\nd" | sed -n 'N;s/^[a-z]$/X/p' ~/pro/3.System_Config/tools/sed$ echo -e "a\nb\nc\nd" | sed -n 'N;s/^[a-z]$/X/Mp' X b X d ~/pro/3.System_Config/tools/sed$
sed首先将 a 读入pattern space,接着执行N命令,此时pattern space中的内容为 a\nb ,第一个sed命令没有使用M修饰符, /^[a-z]$/ 中的 $ 不能匹配 \n 前面的位置,所以匹配失败,不执行替换和打印命令。第二个sed命令使用了M修饰符,成功匹配,即 /^[a-z]$/ 匹配了 a (其中的$匹配了 a 和 \n 中间的位置),将其替换为 X ,此时pattern space中的内容为 X\nb ,然后用p命令打印。注意由于在用N命令将 b 所在行加到pattern space的时候删去了 b 所在行尾的换行符,所以在打印完 b 字符后sed会添加一个换行符,就形成了结果中的前两行的输入,后续的执行过程相同。
如果没有指定行号匹配条件,则所有的行都匹配。如果指定了行号或者正则表达式,则只有与之匹配的行才选择。
另外还可以用范围来选择特定的行,范围是用逗号分割的两个表达式,表达式可以用上面的任意一种方式指定。如果范围内的第二个值小于等于第一个值的话,那么只有与第一个值所匹配的单行匹配。
GNU sed支持几种特殊的指定地址范围的方法:
0,/REGEXP/
这种方式会使/REGEXP/从第一行开始匹配,而常规的1,/REGEXP/则只能让/REGEXP/从第二行开始匹配。
当/REGEXP/匹配第一行时,这两种方式的差异就会体现出来。
例:比较0,/REGEXP/和1,/REGEXP/
~/pro/3.System_Config/tools/sed$ cat a.txt abcdef xxx yyy abcdef xxx yyy ~/pro/3.System_Config/tools/sed$ sed -n '1,/abc/p' a.txt abcdef xxx yyy abcdef ~/pro/3.System_Config/tools/sed$ sed -n '0,/abc/p' a.txt abcdef
ADDR1,+N
匹配从第ADDR1行以及以后的N行。
ADDR1,~N
匹配从第ADDR1行开始,直到N的倍数行。
例:比较ADDR1,+N和ADDR1,~N
~/pro/3.System_Config/tools/sed$ cat c.txt abcdef xxx yyy zzz ~/pro/3.System_Config/tools/sed$ sed -n '1,+3p' c.txt abcdef xxx yyy zzz ~/pro/3.System_Config/tools/sed$ sed -n '1,~3p' c.txt abcdef xxx yyy
sed的命令行选项¶
-n
默认情况下,sed会打印pattern space中的内容,这个选项禁止sed的默认打印行为。
-e SCRIPT
指定欲执行的命令,可以同时指定多个。sed会对pattern space中的内容依次执行每个-e选项指定的命令。
~/pro/3.System_Config/tools/sed$ echo -e "abc\nbcd\ncde" | sed -n -e '/b/p' -e '/d/p' abc bcd bcd cde
可以看到”bcd”所在的行会被打印两次。
-f SCRIPT-FILE
指定执行文件中的命令。
-i [SUFFIX]
默认情况下sed不会修改输入的源文件,会将内容打印到标准输出。
使用了-i选项并且没有SUFFIX参数的时候,它会将输出保存到一个临时文件,当所有命令执行完成之后,将临时文件重命名为输入的源文件。
如果指定了SUFFIX,会将输入的源文件作一个备份。当SUFFIX里不包含星号时,备份的文件名为源文件名+SUFFIX;当SUFFIX里含一个或者多个星号时,会将星号替换为源文件名,这种方式可以给备份文件加上前缀,而不是简单地加一个后缀,甚至SUFFIX里可以包含路径名,这样就可以将源文件备份到另一个文件夹里(前提是那个路径需存在)。
例:-i选项的值SUFFIX不含星号的情况
~/pro/3.System_Config/tools/sed$ ls *c* c.txt ~/pro/3.System_Config/tools/sed$ cat c.txt abcdef xxx yyy zzz ~/pro/3.System_Config/tools/sed$ sed -ibackup 's/^.//g' c.txt ~/pro/3.System_Config/tools/sed$ ls *c* c.txt c.txtbackup ~/pro/3.System_Config/tools/sed$ cat c.txt bcdef xx yy zz ~/pro/3.System_Config/tools/sed$ cat c.txtbackup abcdef xxx yyy zzz
例:-i选项的值SUFFIX包含星号的情况
~/pro/3.System_Config/tools/sed$ cat c.txt abcdef xxx yyy zzz ~/pro/3.System_Config/tools/sed$ ls *c* c.txt ~/pro/3.System_Config/tools/sed$ sed -ibackup* 's/^.//g' c.txt ~/pro/3.System_Config/tools/sed$ ls *c* backupc.txt c.txt ~/pro/3.System_Config/tools/sed$ cat c.txt bcdef xx yy zz ~/pro/3.System_Config/tools/sed$ cat backupc.txt abcdef xxx yyy zzz
-l N, –line-length=N
指定用l命令(sed有一个l命令,不是这里的-l选项,注意区别)显示时的最长行的长度,如果文本大于这个长度会被折行处理。折行时行尾会加一个反斜杠转义换行符。N的长度默认为70。
例:-l N选项
本例中d.txt只包含一行内容,为100个字符a。
~/pro/3.System_Config/tools/sed$ cat d.txt aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ~/pro/3.System_Config/tools/sed$ sed -n 'l' d.txt aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa$ ~/pro/3.System_Config/tools/sed$ sed -n -l 50 'l' d.txt aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aa$
–posix
相对于POSIX sed,GNU sed使用了一些扩展的功能,如果指定这个选项会更加方便移植,但同时会禁用这些扩展功能。
-b, –binary
将输入文件文件当作二进制文件来处理。在dos/windows平台上,由于行分隔符是由CR和LF两个字符组成,如果不指定这个选项,sed也会把这个两个字符的组合当成行分隔符。如果指定了这个选项,sed会将LF当作换行符,这样CR就成了每行的最后一个字符。
–follow-symlinks
是否跟随符号链接。这个选项只在支持符号链接的平台上存在,并且只有同时使用了-i选项时才会起作用。如果输入文件是到另一个文件符号链接,指定了这个选项会跟随符号链接。默认情况下不跟随。
-r, –regexp-extended
使用扩展的正则表达式。
-s, –separate
默认情况下,sed会将命令行指定的所有输入文件当作一个连续的输入流来看待。这个选项会禁止这个特征,将文件独立对待,这会影响某些匹配选项。例如$默认情况下会匹配整个输入流的最后一行,如果指定了-s选项,$会匹配每个文件的最后一行。
-u, –unbuffered
最小化输入和输出的缓冲区大小。如果输入是从类似于tail -f这种管道取得的,这个选项会尽可能快地输出结果。
sed命令¶
常用命令¶
#
注释
q [EXIT-CODE]
q命令会让sed在打印完当前pattern space中的内容后立即退出,如果没有用-n选项来禁止默认的打印行为的话。使用可选的[EXIT-CODE]代码作为sed的退出码。
这个命令只能用于单个地址的情况。
d
删除当前pattern space中的内容并立即开始下一个循环,即sed开始处理下一行。
p
将当前pattern space中的内容打印到标准输入,这个选项一般与-n选项一起使用。
n
n命令会立即用读取的下一行的内容取代当前pattern space中的内容。如果没有用-n选项来禁止默认的打印行为的话,n命令会先打印完当前pattern space中的内容。如果到了输入的末尾,即不能读取更多的内容,则sed立即退出,不执行n之后的命令。
{ COMMAND }
{和}中间可以包含一组命令,用这种方式可以将这一组命令同时作用到某一个地址上。
替换命令¶
s/REGEXP/REPLACEMENT/FLAGS
其中的/可以使用其它单个字符替换。
REPLACEMENT中可以使用一些特殊的字符,这些字符有特殊的含义。
N
引用前面匹配的第N个捕获性括号中的内容。
L
将替换的内容都转换成小写字母,直到遇到U或者E。
l
将下一个字符转换成小写字母。
U
将替换的内容都转换成大写字母,直到遇到L或者E。
E
停止L或者U的字母大小写转换。
FLAGS为替换命令的修饰符。可以包含一个或者多个以下字符。
g
全局替换。默认为只替换第一个匹配的项。
NUMBER
替换第NUMBER个匹配的项。
U
将替换的内容都转换成大写字母,直到遇到L或者E。
E
停止L或者U的字母大小写转换。
p
w FILE-NAME
如果成功替换,将结果输出到FILE-NAME文件中。
e
这是GNU sed的一个扩展命令,如果成功执行了替换操作,则替换的结果会作为一个shell命令来执行,然后将命令运行的输出存入pattern space。
~/pro/3.System_Config/tools/sed$ echo "date" | sed -n 's/^//ep' Sat Oct 22 19:46:32 CST 2011 ~/pro/3.System_Config/tools/sed$ cat g.txt ls -l ~/pro/3.System_Config/tools/sed$ sed -n 's/^//ep' g.txt total 156 -rw-r--r-- 1 forfun forfun 67 Oct 17 17:21 Makefile -rw-r--r-- 1 forfun forfun 30 Oct 21 12:33 a.txt ...... -rw-r--r-- 1 forfun forfun 22598 Oct 22 19:49 sed.xml -rw-r--r-- 1 forfun forfun 19550 Oct 21 15:10 sed.xml~ -rw-r--r-- 1 forfun forfun 35130 Oct 8 15:05 sed1line.txt ~$ echo "xxx12yyy" | sed -n -r 's/(.*?)([0-9]+)(.*?)/echo \1; echo \2+1|bc; echo \3/e; s/\n//gp' xxx13yyy
I, i
忽略大小写。
M, m
即匹配多行,与行匹配的M修饰符作用相同,见M修饰符。
其它命令¶
y/SOURCE-CHARS/DEST-CHARS/
将SOURCE-CHARS字符转换为DEST-CHARS中的字符,二者包含字符的数量必须相同。
~/pro/3.System_Config/tools/sed$ echo "abc" | sed 'y/ab/AB/' ABc
a\ TEXT
这是一个GNU扩展特性,这个命令接受两个地址。
其中的a表示append,这个命令会在处理完当前行或者在处理下一个输入行之前,将TEXT作为一个单独的行进行输出。
在命令行输入时可以将TEXT作为单独一行进行输入,则需要在上一行中a命令后面跟一个 \ 转义符。也可以将TEXT紧接着a命令,中间无需转义符。下面的i和c命令类似。
forfun@mygentoo ~/pro/4.Tools/sed $ echo -e "a\nb\nc" | sed '1,2a\ > xyz' a xyz b xyz c forfun@mygentoo ~/pro/4.Tools/sed $ echo -e "a\nb\nc" | sed '1,2axyz' a xyz b xyz c forfun@mygentoo ~/pro/4.Tools/sed $
i\ TEXT
这是一个GNU扩展特性,这个命令接受两个地址。
其中的i表示insert,这个命令会在处理完当前行之前,将TEXT作为一个单独的行立即输出。
forfun@mygentoo ~/pro/4.Tools/sed $ echo -e "a\nb\nc" | sed '1,2i\ > xyz' xyz a xyz b c forfun@mygentoo ~/pro/4.Tools/sed $ echo -e "a\nb\nc" | sed '1,2ixyz' xyz a xyz b c forfun@mygentoo ~/pro/4.Tools/sed $
c\ TEXT
这个命令接受行地址或者行地址范围。
其中的c表示change,这个命令会将匹配的行删除,同时将TEXT作为一个单独的行输出。实际上就是把匹配行变成TEXT进行输出。
forfun@mygentoo ~/pro/4.Tools/sed $ echo -e "a\nb\nc" | sed '1,2c\ > xyz' xyz c forfun@mygentoo ~/pro/4.Tools/sed $ echo -e "a\nb\nc" | sed '1,2cxyz' xyz c forfun@mygentoo ~/pro/4.Tools/sed $
=
这是一个GNU扩展特性,这个命令接受两个地址。
这个命令会立即打印当前输入行的行号,后面接一个换行符。
~/pro/3.System_Config/tools/sed$ echo -e "a\nb\nc" | sed '1,2=' 1 a 2 b c
l N
使用无歧义的格式打印pattern space中的内容:非打印字符和 \ 字符会打印成c语言的转义形式,比较长的行会使用 \ 折断,每一行末尾用 $ 表示。
N 指定断行后最长行的长度(包括末尾的 \ 字符),如果为0则表示不进行断行,如果忽略,则使用在命令行选项中指定默认值(见sed的 -l 选项),这是一个GNU扩展特性。
forfun@mygentoo ~/pro/4.Tools/sed $ echo -e "\t\b" forfun@mygentoo ~/pro/4.Tools/sed $ echo -e "\t\b" | sed 'l' \t\b$ forfun@mygentoo ~/pro/4.Tools/sed $ cat d.txt aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa forfun@mygentoo ~/pro/4.Tools/sed $ cat d.txt | wc -c 101 forfun@mygentoo ~/pro/4.Tools/sed $ sed -n 'l' d.txt aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa$ forfun@mygentoo ~/pro/4.Tools/sed $ sed -n 'l 50' d.txt aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aa$ forfun@mygentoo ~/pro/4.Tools/sed $
r FILENAME
这是一个GNU扩展特性,这个命令接受两个地址。
其中的r表示read,这个命令会在处理完当前行或者在处理下一个输入行之前,将文件FILENAME中的内容插入当前输入流中。注意如果不能读取文件FILENAME,则将其看作一个空文件,不会报错。
命令r和文件FILENAME之间可以使用或者不使用空格进行分割,下面的w命令相同。
forfun@mygentoo ~/pro/4.Tools/sed $ cat c.txt xxx yyy zzz forfun@mygentoo ~/pro/4.Tools/sed $ echo -e "a\nb\nc" | sed '1,2r c.txt' a xxx yyy zzz b xxx yyy zzz c forfun@mygentoo ~/pro/4.Tools/sed $ echo -e "a\nb\nc" | sed '1,2rc.txt' a xxx yyy zzz b xxx yyy zzz c
另外 /dev/stdin 可以作为文件名。但注意这是一次性的。
forfun@mygentoo ~/pro/4.Tools/sed $ cat b.txt a b c forfun@mygentoo ~/pro/4.Tools/sed $ echo -e 'xx\nyy' | sed '1,3r /dev/stdin' b.txt a xx yy b c forfun@mygentoo ~/pro/4.Tools/sed $
w FILENAME
~/pro/3.System_Config/tools/sed$ echo -e "a\nb\nc" | sed '1,2w h.txt' a b c ~/pro/3.System_Config/tools/sed$ cat h.txt a b
D
删除pattern space中的内容
~/pro/3.System_Config/tools/sed$ cat i.txt abc 123 def456 ghi ~/pro/3.System_Config/tools/sed$ sed '/[a-z]/D' i.txt 123
N
在pattern space中的内容后面增加一个换行符,然后将输入流中的下一行附加到pattern space后面。如果没有更多输入,则sed会退出,不会再执行任何命令。
P
打印pattern space中的内容,直到第一个换行符。
~/pro/3.System_Config/tools/sed$ echo -e "a\nb\nc\nd" | sed 'N;P' a a b c c d
h
将hold space的内容替换为pattern space中的内容。
H
先向hold space中添加一个换行符,接着将pattern space中的内容加到后面。
g
将pattern space的内容替换为hold space中的内容。
G
G命令会将一个换行符和hold space的内容附加到pattern space后面。
x
交换pattern space和hold space中的内容。
跳转命令¶
: LABEL
标记为一个标签
b LABEL
无条件跳转到LABEL。如果不指定LABEL,则开始下一个操作循环。
t LABEL
如果发生了成功的替换操作(s命令),则跳转到LABEL。如果不指定LABEL,则 开始下一个操作循环。
扩展命令¶
e [COMMAND]
此命令的作用是将shell命令的结果通过管道送到pattern space中。如果不指 定命令,则将pattern space中的内容作为命令来执行,并用命令执行的输出替 换pattern space中原先的内容,注意会删掉末尾的换行符。
如果指定了命令,则会执行此命令,并将命令的输出发送到标准输出流,类似 于(‘r’)命令。
Q [EXIT-CODE]
此命令只接受单一地址。作用类似于q命令,会返回一个错误码并退出,不同 的是这个命令不会打印pattern space中的内容。
R FILENAME
在当前操作循环完成后或者读入下一个输入行时,将FILENAME中的内容插入到 当前的输出流中。可以将/dev/stdin作为文件名,结果是会从标准输入读取一 行。
T LABEL
如果发生了不成功的替换操作(s命令),则跳转到LABEL。如果不指定LABEL,则 开始下一个操作循环。
v VERSION
如果当前sed不支持GNU sed扩展,则sed以失败退出。另外,可以用VERSION指 定当前sed需要的版本号。
W FILENAME
将pattern space中从开始到第一个换行符之间的内容写入FILENAME文件中。
z
清空pattern space的内容,作用与s/.*//相同,但效率更高。