文本处理三剑客之sed

sed 编辑器是 Linux 系统管理员的工具包中最有用的资产之一,所以,有必要完全地了解其应用。正则表达式

Linux 操做系统最大的一个好处是它带有各类各样的实用工具。存在如此之多不一样的实用工具,几乎不可能知道并了解全部这些工具。能够简化关键状况下操做的一个实用工具是 sed。它是任何管理员的工具包中最强大的工具之一,而且能够证实它本身在关键状况下很是有价值。shell

sed 实用工具是一个“编辑器”,但它与其它大多数编辑器不一样。除了不面向屏幕以外,它仍是非交互式的。这意味着您必须将要对数据执行的命令插入到命令行或要处理的脚本中。当显示它时,请忘记您在使用 Microsoft Word 或其它大多数编辑器时拥有的交互式编辑文件功能。sed 在一个文件(或文件集)中非交互式、而且不加询问地接收一系列的命令并执行它们。于是,它流经文本就如同水流经溪流同样,于是 sed 恰当地表明了流编辑器。它能够用来将全部出现的 “Mr. Smyth” 修改成 “Mr. Smith”,或将 “tiger cub” 修改成 “wolf cub”。流编辑器很是适合于执行重复的编辑,这种重复编辑若是由人工完成将花费大量的时间。其参数可能和一次性使用一个简单的操做所需的参数同样有限,或者和一个具备成千上万行要进行编辑修改的脚本文件同样复杂。sed Linux UNIX 工具箱中最有用的工具之一,且使用的参数很是少。缓存

 

sed工做流程编辑器

一、 读新的一行到缓存空间。工具

二、 从指定的操做指令中取出第一条指令,判断是否匹配patternthis

三、 若是不匹配则忽略后续的编辑命令,回到2步继续取出下一条指令spa

四、 若是匹配,则针对缓存的行执行后续的编辑命令;完成后,回到第2步继续取出下一条指令;操作系统

五、 当全部指令都应用以后,输出缓存行的内容;回到第1步继续读入下一行内容 命令行

六、 当全部行都处理完以后,结束;regexp

因而可知,sed并不是是将一个编辑命令分别应用到每一行,而后再取下一个编辑命令。偏偏相反,sed是以行的方式来处理的。另一方面,每一行都是被读入到一块缓存空间,该空间名为模式空间(pattern space),这是一个很重要的概念,在后文中会屡次被说起。所以sed操做的都是最初行的拷贝,同时后续的编辑命令都是应用到前面的命令编辑后输出的结果,因此编辑命令之间的顺序就显得格外重要。

 

sed语法

sed [options] '{Addredd command}' [filename]

option:

-n:只打印匹配的行

-i:直接修改读取的的文件内容

-r:使用扩展的正则表达式

-e script –e script:能够同时执行多个脚本

-f:直接将sed的动做写在一个文件内 –f filename则可运行filename内的sed动做

Address

StartLine,EndLine  好比 1,100   $:最后一行

/regexp/: 例如 /^oot/

/pattern1/,/pattern2/ 第一次被pattern1匹配的行到第一次被pattern2匹配的行结束。

LineNum:指定的行

first~step: first行开始,每隔step行执行一次命令

addr1,+N: 从匹配的addr1行开始,连续N行,固然包括addr1这一行

command

a :新增, a 的后面能够接字串,而这些字串会在新的一行出现(目前的下一行)

c :取代, c 的后面能够接字串,这些字串能够取代 n1,n2 之间的行!

d :删除,由于是删除啊,因此 d 后面一般不接任何咚咚;

i  :插入, i 的后面能够接字串,而这些字串会在新的一行出现(目前的上一行)

p :列印,亦即将某个选择的数据印出。一般 p 会与参数 sed -n 一块儿运行~

s :取代,能够直接进行取代的工做哩!一般这个 s 的动做能够搭配正规表示法!例如 1,20s/old/new/g 就是啦!

 

下面开始介绍sed的基础用法

替换命令

sed 实用工具以及其它任何相似的编辑器的最经常使用的命令之一是用一个值替换另外一个值。用来实现这一目的的操做的命令部分语法是:

's/{old value}/{new value}/'
于是,下面演示了如何很是简单地将
“tiger” 修改成 “wolf” 

# echo "The tiger cubs will meet on Tuesday after school" | sed 's/tiger/wolf/'
The wolf cubs will meet on Tuesday after school

若是须要对同一文件或行做屡次修改,能够有三种方法来实现它。第一种是使用 “-e” 选项,它通知程序使用了多条编辑命令。例如:  

注意若是输入是源自以前的命令输出,则不须要指定文件名一样的原则也适用于 awksort 和其它大多数 Linux\UNIX 命令行实用工具程序。

屡次修改

$ echo The tiger cubs will meet on Tuesday after school | sed -e's/tiger/wolf/' -e 's/after/before/'

$ The wolf cubs will meet on Tuesday before school

这是实现它的很是复杂的方法,所以 “-e” 选项不常被大范围使用。更好的方法是用分号来分隔命令:

$ echo The tiger cubs will meet on Tuesday after school | sed ' s/tiger/wolf/; s/after/before/'

$ The wolf cubs will meet on Tuesday before school


注意分号必须是紧跟斜线以后的下一个字符。
若是二者之间有一个空格,操做将不能成功完成,并返回一条错误消息。这两种方法都很好,但许多管理员更喜欢另外一种方法。要注意的一个关键问题是,两个撇号 (‘ ‘) 之间的所有内容都被解释为 sed 命令。直到您输入了第二个撇号,读入这些命令的 shell 程序才会认为您完成了输入。这意味着能够在多行上输入命令同时 Linux 将提示符从 PS1 变为一个延续提示符(一般为 “>”直到输入了第二个撇号。一旦输入了第二个撇号,而且按下了 Enter 键,则处理就进行并产生相同的结果,以下所示: 

$ echo The tiger cubs will meet on Tuesday after school | sed '

> s/tiger/wolf/

> s/after/before/'

$ The wolf cubs will meet on Tuesday before school

全局修改 

让咱们开始一次看似简单的编辑。假定在要修改的消息中出现了屡次要修改的项目。默认方式下,结果可能和预期的有所不一样,以下所示:

$ echo The tiger cubs will meet this Tuesday at the same time as the meeting last Tuesday | sed 's/Tuesday/Thursday/'

$ The tiger cubs will meet this Thursday at the same time as the meeting last Tuesday

与将出现的每一个 “Tuesday” 修改成 “Thursday” 相反,sed 编辑器在找到一个要修改的项目并做了修改以后继续处理下一行,而不读整行。sed 命令功能大致上相似于替换命令,这意味着它们都处理每一行中出现的第一个选定序列。为了替换出现的每个项目,在同一行中出现多个要替换的项目的状况下,您必须指定在全局进行该操做:

$ echo The tiger cubs will meet this Tuesday at the same time as the meeting last Tuesday | sed 's/Tuesday/Thursday/g'

$ The tiger cubs will meet this Thursday at the same time as the meeting last Thursday

请记住无论您要查找的序列是否仅包含一个字符或词组,这种对全局化的要求都是必需的。 

sed 还能够用来修改记录字段分隔符。例如,如下命令将把全部的 tab 修改成空格:

sed 's// /g'
其中,第一组斜线之间的项目是一个
tab,而第二组斜线之间的项目是一个空格。做为一条通用的规则,sed 能够用来将任意的可打印字符修改成任意其它的可打印字符。若是您想将不可打印字符修改成可打印字符例如,铃铛修改成单词 “bell”―sed 不是适于完成这项工做的工具(但 tr 是)。 

有时,您不想修改在一个文件中出现的全部指定项目。有时,您只想在知足某些条件时才做修改例如,在与其它一些数据匹配以后才做修改。为了说明这一点,请考虑如下文本文件:

$ cat sample_one

one 1

two 1

three 1

one 1

two 1

two 1

three 1

$

假定但愿用 “2″ 来替换 “1″,但仅在单词 “two” 以后才做替换,而不是每一行的全部位置。经过指定在给出替换命令以前必须存在一次匹配,能够实现这一点:

$ sed '/two/ s/1/2/' sample_one

one 1
two 2
three 1
one 1
two 2
two 2
three 1

如今,使其更加准确: 

$ sed '

> /two/ s/1/2/

> /three/ s/1/3/' sample_one

one 1
two 2
three 3
one 1
two 2
two 2
three 3

请再次记住惟一改变了的是显示。若是您查看源文件,您将发现它始终保持不变。您必须将输出保存至另外一个文件,以实现永久保存。值得重复的是,不对源文件做修改实际是祸中有福它让您可以对文件进行试验而不会形成任何实际的损害,直到让正确命令以您预期和但愿的方式进行工做。

如下命令将修改后的输出保存至一个新的文件:

该输出文件将全部修改合并在其中,而且这些修改一般将在屏幕上显示。如今能够用 headcat 或任意其它相似的实用工具来进行查看。

脚本文件

sed 工具容许您建立一个脚本文件,其中包含从该文件而不是在命令行进行处理的命令,而且 sed 工具经过 “-f” 选项来引用。经过建立一个脚本文件,您可以一次又一次地重复运行相同的操做,并指定比每次但愿从命令行进行处理的操做详细得多的操做。

考虑如下脚本文件:

 

如今能够在数据文件上使用脚本文件,得到和咱们以前看到的相同的结果:

注意当调用 “-f” 选项时,在源文件内或命令行中不使用撇号。脚本文件,也称为源文件,对于想重复屡次的操做和从命令行运行可能出错的复杂命令颇有价值。编辑源文件并修改一个字符比在命令行中从新输入一条多行的项目要容易得多。

限制行

编辑器默认查看输入到流编辑器中的每一行,且默认在输入到流编辑器中的每一行上进行编辑。

能够经过定址来定位你所但愿编辑的行,该地址用数字构成,用逗号分隔的两个行数表示以这两行为起止的行的范围(包括行数表示的那两行)。如13表示123行,美圆符号($)表示最后一行。范围能够经过数据,正则表达式或者两者结合的方式肯定 。

例如,只在此示例文件的输出的第 5 和第 6 行中用 “2″ 来替换 “1″,命令将为:

 

在这种状况下,由于要修改的行是专门指定的,因此不须要替换命令。所以,您能够灵活地根据匹配准则(能够是行号或一种匹配模式)来选择要修改哪些行(从根本上限制修改)。

禁止显示

sed 默认未来自源文件的每一行显示到屏幕上(或重定向到一个文件中),而不管该行是否受到编辑操做的影响,”-n” 参数覆盖了这一操做。”-n” 覆盖了全部的显示,而且不显示任何一行,而不管它们是否被编辑操做修改。例如:

 

在第一个示例中,屏幕上不显示任何东西。在第二个示例中,不修改任何东西,所以不将任何东西写到新的文件中它最后是空的。这不是否认了编辑的所有目的吗?为何这是有用的?它是有用的仅由于 “-n” 选项可以被一条显示命令 (-p) 覆盖。为了说明这一点,假定如今像下面这样对脚本文件进行了修改:

 

而后下面是运行它的结果:

保持不变的行所有不被显示。只有受到编辑操做影响的行被显示了。在这种方式下,能够仅取出这些行,进行修改,而后把它们放到一个单独的文件中:

 

利用它的另外一种方法是只显示必定数量的行。例如,只显示 2-6 行,同时不作其它的编辑修改:

其它全部的行被忽略,只有 2-6 行做为输出显示。这是一项出色的功能,其它任何工具都不能容易地实现。Head 将显示一个文件的顶部,而 tail 将显示一个文件的底部,但 sed 容许从任意位置取出想要的任意内容。

删除行

用一个值替换另外一个值远非流编辑器能够执行的惟一功能。它还具备许多的潜在功能,在我看来第二种最经常使用的功能是删除。删除与替换的工做方式相同,只是它删除指定的行(若是您想要删除一个单词而不是一行,不要考虑删除,而应考虑用空的内容来替换它―s/cat//)。

该命令的语法是: 

‘{what to find} d’

sample_one 文件中删除包含 “two” 的全部行:

 

只显示剩下的行,前三行不在显示屏中出现。对于流编辑器,通常当它们涉及到全局表达式时,特别是应用于删除操做时,有几点要记住:

上三角号 (^) 表示一行的开始,所以,若是 “two” 是该行的头三个字符,则

 

sed '/^two/ d' sample_one

将只删除该行。
美圆符号 ($) 表明文件的结尾,或一行的结尾,所以,若是 “two” 是该行的最后三个字符,则

sed '/two$/ d' sample_one

将只删除该行。

将这二者结合在一块儿的结果:

 

sed '/^$/ d' {filename}

删除文件中的全部空白行。例如,如下命令将 “1″ 替换为 “2″,以及将 “1″ 替换为 “3″,并删除文件中全部尾随的空行:

 

其一般的用途是删除一个标题。如下命令将删除文件中全部的行,从第一行直到第一个空行:

 

sed '1,/^$/ d' {filename}

添加和插入文本

能够结合使用 sed “a” 选项将文本添加到一个文件的末尾。实现方法以下:

 

1

$ sed '$a\

 

2

> This is where we stop\

 

 

 

3

> the test' sample_one

4

one 1

 

 

 

 

5

two 1

 

6

three 1

 

 

 

7

one 1

8

two 1

 

 

 

9

two 1

 

10

three 1

 

 

 

11

This is where we stop

12

the test

 

 

 

 

13

$

 

在该命令中,美圆符号 ($) 表示文本将被添加到文件的末尾。反斜线 (\) 是必需的,它表示将插入一个回车符。若是它们被遗漏了,则将致使一个错误,显示该命令是错乱的;在任何要输入回车的地方您必须使用反斜线。

 

要将这些行添加到第 4 和第 5 个位置而不是末尾,则命令变为:

 

1

$ sed '3a\

 

2

> This is where we stop\

 

 

 

3

> the test' sample_one

4

one 1

 

 

 

 

5

two 1

 

6

three 1

 

 

 

7

This is where we stop

8

the test

 

 

 

 

9

one 1

 

10

two 1

 

 

 

11

two 1

 

12

three 1

 

 

 

13

$

 

这将文本添加到第 3 行以后。和几乎全部的编辑器同样,您能够选择插入而不是添加(若是您但愿这样的话)。这二者的区别是添加跟在指定的行以后,而插入从指定的行开始。当用插入来代替添加时,只需用 “i” 来代替 “a”,以下所示:

 

1

$ sed '3i\

 

2

> This is where we stop\

 

 

 

3

> the test' sample_one

4

one 1

 

 

 

 

5

two 1

 

6

This is where we stop

 

 

 

7

the test

8

three 1

 

 

 

 

9

one 1

 

10

two 1

 

 

 

11

two 1

 

12

three 1

 

 

 

13

$

 

新的文本出如今输出的中间位置,而处理一般在指定的操做执行之后继续进行。

 

读写文件

 

重定向输出的功能已经演示过了,但须要指出的是,在编辑命令运行期间能够同步地读入和写出文件。例如,执行替换,并将 1-3 行写到名称为 sample_three 的文件中:

 

1

$ sed '

 

2

> /two/ s/1/2/

 

 

 

3

> /three/ s/1/3/

 

4

> 1,3 w sample_three' sample_one

 

 

 

5

one 1

6

two 2

 

 

 

7

three 3

8

one 1

 

 

 

 

9

two 2

 

10

two 2

 

 

 

11

three 3

12

$

 

 

 

 

13

 

 

14

$ cat sample_three

 

 

 

15

one 1

16

two 2

 

 

 

17

three 3

18

$

 

 

因为为 w (write) 命令指定了 “1,3″,因此只有指定的行被写到了新文件中。不管被写的是哪些行,全部的行都在默认输出中显示。

 

修改命令

 

除了替换项目以外,还能够将行从一个值修改成另外一个值。要记住的是,替换是对字符逐个进行,而修改功能与删除相似,它影响整行:

 

1

$ sed '/two/ c\

 

2

> We are no longer using two' sample_one

 

 

 

3

one 1

 

4

We are no longer using two

 

 

 

5

three 1

6

one 1

 

 

 

 

7

We are no longer using two

8

We are no longer using two

 

 

 

9

three 1

10

$

 

 

修改命令与替换的工做方式很类似,但在范围上要更大些将一个项目彻底替换为另外一个项目,而不管字符内容或上下文。夸张一点讲,当使用替换时,只有字符 “1″ 被字符 “2″ 替换,而当使用修改时,原来的整行将被修改。在两种状况下,要寻找的匹配条件都仅为 “two”

 

修改所有但……

 

对于大多数 sed 命令,详细说明各类功能要进行何种修改。利用感叹号,能够在除指定位置以外的任何地方执行修改与默认的操做彻底相反。

 

例如,要删除包含单词 “two” 的全部行,操做为:

 

1

$ sed '/two/ d' sample_one

2

one 1

 

 

 

 

3

three 1

4

one 1

 

 

 

 

5

three 1

6

$

 

 

而要删除除包含单词 “two” 的行以外的全部行,则语法变为:

 

1

$ sed '/two/ !d' sample_one

2

two 1

 

 

 

 

3

two 1

4

two 1

 

 

 

5

$

 

若是您有一个文件包含一系列项目,而且想对文件中的每一个项目执行一个操做,那么首先对那些项目进行一次智能扫描并考虑将要作什么是很重要的。为了使事情变得更简单,您能够将 sed 与任意迭代例程(forwhileuntil)结合来实现这一目的。

 

好比说,假定您有一个名为 “animals” 的文件,其中包含如下项目:

 

1

pig

 

2

horse

 

 

 

3

elephant

4

cow

 

 

 

 

5

dog

6

cat

 

您但愿运行如下例程:

 

1

#mcd.ksh

 

2

for I in $*

 

 

 

3

do

 

4

echo Old McDonald had a $I

 

 

 

5

echo E-I, E-I-O

6

done

 

 

结果将为,每一行都显示在 “Old McDonald has a” 的末尾。虽然对于这些项目的大部分这是正确的,但对于 “elephant” 项目,它有语法错误,由于结果应当为 “an elephant” 而不是 “a elephant”。利用 sed,您能够在来自 shell 文件的输出中检查这种语法错误,并经过首先建立一个命令文件来即时地更正它们:

 

1

#sublist

 

2

/ a a/ s/ a / an /

 

 

 

3

/ a e/ s/ a / an /

4

/a i/ s / a / an /

 

 

 

5

/a o/ s/ a / an /

6

/a u/ s/ a / an /

 

而后执行如下过程:

 

1

$ sh mcd.ksh 'cat animals' | sed -f sublist

 

如今,在运行了 mcd 脚本以后,sed 将在输出中搜索单个字母 a (空格,”a”,空格)以后紧跟了一个元音的任意位置。若是这种位置存在,它将把该序列修改成空格,”an”,空格。这样就使问题更正后才显示在屏幕上,并确保各处的编辑人员在晚上能够更容易地入睡。结果是:

 

1

Old McDonald had a pig

2

E-I, E-I-O

 

 

3

Old McDonald had a horse

4

E-I, E-I-O

 

 

5

Old McDonald had an elephant

6

E-I, E-I-O

 

 

7

Old McDonald had a cow

8

E-I, E-I-O

 

 

9

Old McDonald had a dog

10

E-I, E-I-O

 

 

11

Old McDonald had a cat

12

E-I, E-I-O

 

 

提早退出

sed 默认读取整个文件,并只在到达末尾时才中止。不过,您可使用退出命令提早中止处理。只能指定一条退出命令,而处理将一直持续直到知足调用退出命令的条件。

例如,仅在文件的前五行上执行替换,而后退出:

 

1

$ sed '

 

2

> /two/ s/1/2/

 

3

> /three/ s/1/3/

4

> 5q' sample_one

 

5

one 1

6

two 2

 

7

three 3

8

one 1

 

 

9

two 2

10

$

 

 

在退出命令以前的项目能够是一个行号(如上所示),或者一条查找/匹配命令:

1

$ sed '

 

2

> /two/ s/1/2/

 

3

> /three/ s/1/3/

 

4

> /three/q' sample_one

 

5

one 1

6

two 2

 

7

three 3

8

$

 

 

您还可使用退出命令来查看超过必定标准数目的行,并增长比 head 中的功能更强的功能。例如,head 命令容许您指定您想要查看一个文件的前多少行默认数为 10,但可使用从 1 99 的任意一个数字。若是您想查看一个文件的前 110 行,您用 head 不能实现这一目的,但用 sed 能够:

sed 110q filename

 

处理问题

当使用 sed 时,要记住的重要事项是它的工做方式。它的工做方式是:读入一行,在该行上执行它已知要执行的全部任务,而后继续处理下一行。每一行都受给定的每个编辑命令的影响。

若是您的操做顺序没有十分完全地考虑清楚,那么这可能会很麻烦。例如,假定您须要将全部的 “two” 项目修改成 “three”,而后将全部的 “three” 修改成 “four”

 

1

$ sed '

 

2

> /two/ s/two/three/

 

3

> /three/ s/three/four/' sample_one

4

one 1

 

 

5

four 1

6

four 1

 

7

one 1

 

8

four 1

 

9

four 1

 

10

four 1

 

11

$

 

最初读取的 “two” 被修改成 “three”。而后它知足为下一次编辑创建的准则,从而变为 “four”。最终的结果不是想要的结果如今除了 “four” 没有别的项目了,而原本应该有 “three” “four”

当执行这种操做时,您必须很是用心地注意指定操做的方式,并按某种顺序来安排它们,使得操做之间不会互相影响。例如:

1

$ sed '

 

2

> /three/ s/three/four/

 

3

> /two/ s/two/three/' sample_one

4

one 1

 

 

5

three 1

6

four 1

 

 

7

one 1

 

8

three 1

 

 

 

9

three 1

10

four 1

 

11

$

 

这很是有效,由于 “three” 值在 “two” 变成 “three” 以前获得修改。

标签和注释

能够在 sed 脚本文件中放置标签,这样一旦文件变得庞大,能够更容易地说明正在发生的事情。存在各类各样与这些标签相关的命令,它们包括:
接下来的步骤

访问并收藏 Linux 技术中心

阅读 Dale Dougherty Arnold Robbins 的著做 sed & awk, 2nd Edition O’Reilly & Associates 出版社)。

: 冒号表示一个标签名称。例如:

:HERE

以冒号开始的标签能够由 “b” “t” 命令处理。

b {label} 充当 “goto” 语句的做用,将处理发送至前面有一个冒号的标签。例如, 

b HERE

将处理发送给行

:HERE 

若是紧跟 b 以后没有指定任何标签,则处理转至脚本文件的末尾。

t {label} 只要自上次输入行或执行一次 “t” 命令以来进行了替换操做,就转至该标签。和 “b” 同样,若是没有给定标签名,则处理转至脚本文件的末尾。 

# 符号做为一行的第一个字符将使整行被看成注释处理。注释行与标签不一样,不能使用 b t 命令来转到注释行上。

相关文章
相关标签/搜索