shell基础十:sed
时间:2005-08-30 来源:nicolaus
sed 用法介绍
s e d是一个非交互性文本流编辑器。它编辑文件或标准输入导出的文本拷贝。
可以在命令行输入s e d命令,也可以在一个文件中写入命令,然后调用s e d,这与a w k基本相同。使用s e d需要记住的一个重要事实是,无论命令是什么, s e d并不与初始化文件打交道,它操作的只是一个拷贝,然后所有的改动如果没有重定向到一个文件,将输出到屏幕。
因为s e d是一个非交互性编辑器,必须通过行号或正则表达式指定要改变的文本行。
本文介绍s e d用法和功能。本章大多编写的是一行命令和小脚本。这样做可以慢慢加深对s e d用法的了解,取得宝贵的经验,以便最终自己编出大的复杂s e d脚本。
和g r e p与a w k一样, s e d是一种重要的文本过滤工具,或者使用一行命令或者使用管道与g r e p与a w k相结合。
记住在命令行使用s e d命令时,实际命令要加单引号。s e d也允许加双引号。
使用s e d脚本文件,格式为:
要使用第一行具有s e d命令解释器的s e d脚本文件,其格式为:
不管是使用s h e l l命令行方式或脚本文件方式,如果没有指定输入文件, s e d从标准输入中接受输入,一般是键盘或重定向结果。
2.1 保存sed输出
由于不接触初始化文件,如果想要保存改动内容,简单地将所有输出重定向到一个文件即可。下面的例子重定向s e d命令的所有输出至文件‘ m y o u t f i l e’,当对结果很满意时使用这种方法。
2.2 使用sed在文件中查询文本的方式
s e d浏览输入文件时,缺省从第一行开始,有两种方式定位文本:
下面是使用s e d定位文本的一些方式。
2.3 基本sed编辑命令
sed和正则表达式
s e d识别任何基本正则表达式和模式及其行匹配规则。记住规则之一是:如果要定位一特殊字符,必须使用( )屏蔽其特殊含义
s e d例子中使用下述文本文件q u o t e . t x t。
1 使用p(rint)显示行
只打印第二行,用-n
2 打印范围
可以指定行的范围,现打印1到3行,用逗号分隔行号。
3 打印模式
假定要匹配单词N e a v e,并打印此行,方法如下。使用模式/ p a t t e r n /格式,这里为/ N e a v e /。
4 使用模式和行号进行查询
可以将行号和模式结合使用。假定要改动文件q u o t e . t x t最后一行中的单词t h e,使用s e d查询t h e,返回两行:
使用模式与行号的混合方式可以剔除第一行,格式为l i n e _ n u m b e r, / p a t t e r n /。逗号用来分隔行号与模式开始部分。为达到预期结果,使用4 , / t h e /。意即只在第四行查询模式t h e,命令如下:
上面有错,其实是把第四行后的都打出来了
这个模式应该哪果指定行找不到符合条件的,就从下一行开始查找,直到找到为止,并把,找到行之前的全部打打印出来。
如果指定行本身就符合条伯,把本行及后面的行的全部打印出来
5 匹配元字符
匹配元字符$前,必须使用反斜线屏蔽其特殊含义。模式为/$/ p。
6 显示整个文件
要打印整个文件,只需将行范围设为第一行到最后一行1 , $。$意为最后一行。
7 任意字符
匹配任意字母,后跟任意字母的0次或多次重复,并以i n g结尾,模式为/ . * i n g /。可以使用这个模式查询以i n g结尾的任意单词。
8 首行
要打印文件第一行,使用行号:
9 最后一行
要打印最后一行,使用$。$是代表最后一行的元字符。
10 打印行号
要打印行号,使用等号=。打印模式匹配的行号,使用格式/ p a t t e r n / =。
整个文件都打印出来,并且匹配行打印了行号。如果只关心实际行号,使用- e选项。
如果只打印行号及匹配行,必须使用两个s e d命令,并使用e选项。第一个命令打印模式匹配行,第二个使用=选项打印行号,格式为sed -n -e /pattern/p -e /pattern/=
11 附加文本
要附加文本,使用符号a ,可以将指定文本一行或多行附加到指定行。如果不指定文本放置位置, s e d缺省放在每一行后面。附加文本时不能指定范围,只允许一个地址模式。文本附加操作时,结果输出在标准输出上。注意它不能被编辑,因为s e d执行时,首先将文件的一行文本拷贝至缓冲区,在这里s e d编辑命令执行所有操作(不是在初始文件上),因为文本直接输出到标准输出,s e d并无拷贝。
要想在附加操作后编辑文本,必须保存文件,然后运行另一个s e d命令编辑它。这时文件的内容又被移至缓冲区。
附加操作格式如下:
地址指定一个模式或行号,定位新文本附加位置。a 通知s e d对a 后的文本进行实际附加操作。观察格式,注意每一行后面有一斜划线,这个斜划线代表换行。s e d执行到这儿,将创建一新行,然后插入下一文本行。最后一行不加斜划线, s e d假定这是附加命令结尾。
当附加或插入文本或键入几个s e d命令时,可以利用辅助的s h e l l提示符以输入多行命令。当附加或插入文本或键入几个s e d命令时,可以利用辅助的s h e l l提示符以输入多行命令。
创建sed脚本文件
创建脚本文件a p p e n d . s e d:
第一行是s e d命令解释行。脚本在这一行查找s e d以运行命令,这里定位在/ b i n。
第二行以/ c o m p a n y /开始,这是附加操作起始位置。a 通知s e d这是一个附加操作,首先应插入一个新行。
第三行是附加操作要加入到拷贝的实际文本。
输出显示附加结果。如果要保存输出,重定向到一个文件。
保存它,增加可执行权限,运行
或直接用命令行:
[sam@chenwy sam]$ sed "/company/iutter confusion followed." quote.txt
The honeysuckle band played all night long for only $90.
utter confusion followed.
It was an evening of splendid music and company.
Too bad the disco floor fell through at 23:00.
The local nurse Miss P.Neave was in attendance.
插入文本:
插入命令类似于附加命令,只是在指定行前面插入。和附加命令一样,它也只接受一个地址。
如在a t t e n d a n c e结尾的行前插入文本utter confusion followed。
也可以指定行:
执行结果
修改文本
修改命令将在匹配模式空间的指定行用新文本加以替代,格式如下:
将第一行The honeysuckle band played all night long for only $90替换为The office Di b b l e band played well。首先要匹配第一行的任何部分,可使用模式‘ / H o n e y s u c k l e /’。s e d脚本文件为c h a n g e . s e d。内容如下:
或命令行:
可以对同一个脚本中的相同文件进行修改、附加、插入三种动作匹配和混合操作。
删除文本
s e d删除文本格式:
删除第一行;1 d意为删除第一行。
删除第一到第三行:
删除最后一行:
也可以使用正则表达式进行删除操作。下面的例子删除包含文本‘ N e a v e’的行。
替换文本
替换命令用替换模式替换指定模式,格式为:
s选项通知s e d这是一个替换操作,并查询p a t t e r n - t o - f i n d,成功后用r e p l a c e m e n t - p a t t e r n替换它。
替换选项如下:
如替换n i g h t为N I G H T,首先查询模式n i g h t,然后用文本N I G H T替换它。
要从$ 9 0 中删除$ 符号(记住这是一个特殊符号,必须用 屏蔽其特殊含义),在r e p l a c e m e n t - p a t t e r n部分不写任何东西,保留空白,但仍需要用斜线括起来。在s e d中也可以这样删除一个字符串。
要进行全局替换,即替换所有出现模式,只需在命令后加g选项。下面的例子将所有T h e替换成Wo w!。
将替换结果写入一个文件用w选项,下面的例子将s p l e n d i d替换为S P L E N D I D的替换结果写入文件s e d . o u t:
注意要将文件名括在s e d的单引号里。文件结果如下:
使用替换修改字符串
如果要附加或修改一个字符串,可以使用(&)命令,&命令保存发现模式以便重新调用它,然后把它放在替换字符串里面。
先给出一个被替换模式,然后是一个准备附加在第一个模式后的另一个模式,并且后面带有&,这样修改模式将放在匹配模式之前。
例如, s e d语句s/nurse/"Hello"&/p 的结果如下
原句是文本行The local nurse Miss P.Neave was in attendance。
记住模式中要使用空格,因为输出结果表明应加入空格。
还有一个例子:
原句是The honeysuckle band played all night long for only $90。
将sed结果写入文件命令
像使用>文件重定向发送输出到一个文件一样,在s e d命令中也可以将结果输入文件。格式有点像使用替换命令:
‘w’选项通知s e d将结果写入文件。f i l e n a m e是自解释文件名。
下面有两个例子。
文件q u o t e . t x t输出到屏幕。模式范围即1,2行输出到文件f i l e d t。
下面例子中查询模式N e a v e,匹配结果行写入文件f i l e d h t。
从文件中读文本
处理文件时, s e d允许从另一个文件中读文本,并将其文本附加在当前文件。此命令放在模式匹配行后,格式为:
这里r通知s e d将从另一个文件源中读文本。f i l e n a m e是其文件名。
现在创建一个小文件s e d e x . t x t,内容如下:
将s e d e x . t x t内容附加到文件q u o t e . t x t的拷贝。在模式匹配行/ c o m p a n y /后放置附加文本。本例为第三行。注意所读的文件名需要用单引号括起来。
匹配后退出
有时需要在模式匹配首次出现后退出s e d,以便执行其他处理脚本。退出命令格式为:
下面的例子假定查询模式/ . a . * /,意为任意字符后跟字符a,再跟任意字符0次或任意多次。
查询首次出现模式,然后退出。需要将q放在s e d语句末尾。
显示文件中的控制字符
1、$vi dos.txt
进入vi后,用ctrl+v 再用ctrl+M产生控制字符^M不知对不对
使用cat -v filename命令查看编辑好的文件
s e d格式为:
‘l’意为列表。一般情况下要列出整个文件,而不是模式匹配行,因此使用l要从第一到最后一行。模式范围1,$即为此意。
处理控制字符
使用s e d实现的一个重要功能是在另一个系统中下载的文件中剔除控制字符。
下面是传送过来的文件( d o s . t x t)的部分脚本。必须去除所有可疑字符,以便于帐号所有者使用文件。
删除所有的#字符很容易,可以使用全局替换命令。这里用一个空格替换两个或
更多的#符号。
。删除所有行首的0。使用^符号表示模式从行首开始, ^ 0 *表示行首任意个0。模式s / ^ 0 * / / g设置替换部分为空,即为删除模式,正是要求所在。
最后去除行尾^ M符号,为此需做全局替换。设置替换部分为空。模式为:
‘s / ^ m / / g’,注意‘^ M’,这是一个控制字符。
在命令行里也必须用^M控制字符耶!?
或
处理报文输出
当从数据库中执行语句输出时,一旦有了输出结果,脚本即可做进一步处理。通常先做一些整理,下面是一个s q l查询结果。
为了使用上述输出信息做进一步自动处理,需要知道所存数据库名称,为此需执行以下
操作:
1) 使用s / - * / / g删除横线- - - - - -。
2) 使用/ ^ $ / d删除空行。
3) 使用$ d删除最后一行
4) 使用1 d删除第一行。
5) 使用awk {print $1}打印第一列。
命令如下,这里使用了c a t,并管道传送结果到s e d命令。
附加文本
当帐户完成设置一个文件时,帐号管理者可能要在文件中每个帐号后面加一段文字,下面是此类文件的一部分:
任务是在每一行末尾加一个字符串‘ p a s s e d’。
使用$命令修改各域会使工作相对容易些。首先需要匹配至少两个或更多的数字重复出现,这样将所有的帐号加进匹配模式。
从shell向sed传值
要从命令行中向s e d传值,值得注意的是用双引号,否则功能不执行。
从sed输出中设置shell变量
从s e d输出中设置s h e l l变量是一个简单的替换过程。运用上面的例子,创建s h e l l变量N E W- N A M E,保存上述s e d例子的输出结果。
这里的`是键盘左上角那个`
下面是一些一行命令集。([ ]表示空格, [ ]表示t a b键)
1. 删除路径名第一个符号
将当前工作目录返回给s e d,删除第一个:
2. 追加/插入文本
将"Mr Wi l l i s "字串返回给s e d并在M r后而追加" B r u c e "。
3. 删除首字符
s e d删除字符串“a c c o u n t s . d o c”首字符。
4. 删除文件扩展名
s e d删除“a c c o u n t s . d o c”文件扩展名。
5. 增加文件扩展名
s e d附加字符串“. d o c”到字符串“a c c o u n t s”。
6. 替换字符系列
如果变量x含有下列字符串:
如果要加入of,located,并去掉+,&实现下列转换:
把+用 of 替换,&用located at 替换
挑选编辑器
在 UNIX 世界中有很多文本编辑器可供我们选择。思考一下 -- vi、emacs 和 jed 以及很多其它工具都会浮现在脑海中。我们都有自己已逐渐了解并且喜爱的编辑器(以及我们喜爱的组合键)。有了可信赖的编辑器,我们可以轻松处理任何数量与 UNIX 有关的管理或编程任务。
虽然交互式编辑器很棒,但却有其限制。尽管其交互式特性可以成为强项,但也有其不足之处。考虑一下需要对一组文件执行类似更改的情形。您可能会本能地运行自己所喜爱的编辑器,然后手工执行一组烦琐、重复和耗时的编辑任务。然而,有一种更好的方法。
进入 sed
如果可以使编辑文件的过程自动化,以便用“批处理”方式编辑文件,甚至编写可以对现有文件进行复杂更改的脚本,那将太好了。幸运的是,对于这种情况,有一种更好的方法 -- 这种更好的方法称为 "sed"。
sed 是一种几乎包括在所有 UNIX 平台(包括 Linux)的轻量级流编辑器。sed 有许多很好的特性。首先,它相当小巧,通常要比您所喜爱的脚本语言小很多倍。其次,因为 sed 是一种流编辑器,所以,它可以对从如管道这样的标准输入接收的数据进行编辑。因此,无需将要编辑的数据存储在磁盘上的文件中。因为可以轻易将数据管道输出到 sed,所以,将 sed 用作强大的 shell 脚本中长而复杂的管道很容易。试一下用您所喜爱的编辑器去那样做。
GNU sed
对 Linux 用户来说幸运的是,最好的 sed 版本之一恰好是 GNU sed,其当前版本是 3.02。每一个 Linux 发行版都有(或至少应该有)GNU sed。GNU sed 之所以流行不仅因为可以自由分发其源代码,还因为它恰巧有许多对 POSIX sed 标准便利、省时的扩展。另外,GNU 没有 sed 早期专门版本的很多限制,如行长度限制 -- GNU 可以轻松处理任意长度的行。
最新的 GNU sed
在研究这篇文章之时我注意到:几个在线 sed 爱好者提到 GNU sed 3.02a。奇怪的是,在ftp.gnu.org(有关这些链接,请参阅参考资料)上找不到 sed 3.02a,所以,我只得在别处寻找。我在alpha.gnu.org 的 /pub/sed 中找到了它。于是我高兴地将其下载、编译然后安装,而几分钟后我发现最新的 sed 版本却是 3.02.80 -- 可在alpha.gnu.org 上 3.02a 源代码旁边找到其源代码。安装完 GNU sed 3.02.80 之后,我就完全准备好了。
alpha.gnu.org
alpha.gnu.org(请参阅参考资料)是新的和实验性 GNU 源代码的所在地。然而,您还会在那里发现许多优秀、稳定的源代码。出于某种原因,不是许多 GNU 开发人员忘记将稳定的源代码移至 ftp.gnu.org,就是它们的 "beta" 期间格外长(2 年!)。例如,sed 3.02a 已有两年,甚至 3.02.80 也有一年,但它们仍不能(在 2000 年 8 月写本文章时)在 ftp.gnu.org 上获得。
正确的 sed
在本系列中,将使用 GNU sed 3.02.80。在即将出现的本系列后续文章中,某些(但非常少)最高级的示例将不能在 GNU sed 3.02 或 3.02a 中使用。如果您使用的不是 GNU sed,那么结果可能会不同。现在为什么不花些时间安装 GNU sed 3.02.80 呢?那样,不仅可以为本系列的余下部分作好准备,而且还可以使用可能是目前最好的 sed。
sed 示例
sed 通过对输入数据执行任意数量用户指定的编辑操作(“命令”)来工作。sed 是基于行的,因此按顺序对每一行执行命令。然后,sed 将其结果写入标准输出 (stdout),它不修改任何输入文件。
让我们看一些示例。头几个会有些奇怪,因为我要用它们演示 sed 如何工作,而不是执行任何有用的任务。然而,如果您是 sed 新手,那么理解它们是十分重要的。下面是第一个示例:
如果输入该命令,将得不到任何输出。那么,发生了什么?
在该例中,用一个编辑命令 'd' 调用 sed。sed 打开 /etc/services 文件,将一行读入其模式缓冲区,执行编辑命令(“删除行”),然后打印模式缓冲区(缓冲区已为空)。然后,它对后面的每一行重复这些步骤。这不会产生输出,因为 "d" 命令除去了模式缓冲区中的每一行!
在该例中,还有几件事要注意。首先,根本没有修改 /etc/services。这还是因为 sed 只读取在命令行指定的文件,将其用作输入 -- 它不试图修改该文件。第二件要注意的事是 sed 是面向行的。'd' 命令不是简单地告诉 sed 一下子删除所有输入数据。相反,sed 逐行将 /etc/services 的每一行读入其称为模式缓冲区的内部缓冲区。一旦将一行读入模式缓冲区,它就执行 'd' 命令,然后打印模式缓冲区的内容(在本例中没有内容)。我将在后面为您演示如何使用地址范围来控制将命令应用到哪些行 -- 但是,如果不使用地址,命令将应用到所有行。
第三件要注意的事是括起 'd' 命令的单引号的用法。养成使用单引号来括起 sed 命令的习惯是个好注意,这样可以禁用 shell 扩展。
另一个 sed 示例
下面是使用 sed 从输出流除去 /etc/services 文件第一行的示例:
地址范围
现在,让我们看一下如何指定地址范围。在本例中,sed 将删除输出的第 1 到 10 行:
当用逗号将两个地址分开时,sed 将把后面的命令应用到从第一个地址开始、到第二个地址结束的范围。在本例中,将 'd' 命令应用到第 1 到 10 行(包括这两行)。所有其它行都被忽略。
带规则表达式的地址
现在演示一个更有用的示例。假设要查看 /etc/services 文件的内容,但是对查看其中包括的注释部分不感兴趣。如您所知,可以通过以 '#' 字符开头的行在 /etc/services 文件中放置注释。为了避免注释,我们希望 sed 删除以 '#' 开始的行。以下是具体做法:
让我们分析发生的情况。
要理解 '/^#/d' 命令,首先需要对其剖析。首先,让我们除去 'd' -- 这是我们前面所使用的同一个删除行命令。新增加的是 '/^#/' 部分,它是一种新的规则表达式地址。规则表达式地址总是由斜杠括起。它们指定一种 模式,紧跟在规则表达式地址之后的命令将仅适用于正好与该特定模式匹配的行。
因此,'/^#/' 是一个规则表达式。但是,它做些什么呢?很明显,现在该复习规则表达式了。
规则表达式复习
可以使用规则表达式来表示可能会在文本中发现的模式。您在 shell 命令行中用过 '*' 字符吗?这种用法与规则表达式类似,但并不相同。下面是可以在规则表达式中使用的特殊字符:
感受规则表达式的最好方法可能是看几个示例。所有这些示例都将被 sed 作为合法地址接受,这些地址出现在命令的左边。下面是几个示例:
在这些示例中,鼓励您尝试几个。花一些时间熟悉规则表达式,然后尝试几个自己创建的规则表达式。可以如下使用 /^#/:
这将导致 sed 删除任何匹配的行。删除以#开头的行
另一个例子:
请注意新的 '-n' 选项,该选项告诉 sed 除非明确要求打印模式空间,否则不这样做。您还会注意到,我们用 'p' 命令替换了 'd' 命令,如您所猜想的那样,这明确要求 sed 打印模式空间。就这样,将只打印匹配部分。打印以#开头的行
有关地址的更多内容
目前为止,我们已经看到了行地址、行范围地址和 ^# 地址。但是,还有更多的可能。我们可以指定两个用逗号分开的规则表达式,sed 将与所有从匹配第一个规则表达式的第一行开始,到匹配第二个规则表达式的行结束(包括该行)的所有行匹配。例如,以下命令将打印从包含 "BEGIN" 的行开始,并且以包含 "END" 的行结束的文本块:
如果没发现 "BEGIN",那么将不打印数据。如果发现了 "BEGIN",但是在这之后的所有行中都没发现 "END",那么将打印所有后续行。发生这种情况是因为 sed 面向流的特性 -- 它不知道是否会出现 "END"。
C 源代码示例
如果只要打印 C 源文件中的 main() 函数,可输入:
以main后面跟空格或制表键,以(开头,}结尾的
该命令有两个规则表达式 '/main[[:space:]]*(/' 和 '/^}/',以及一个命令 'p'。第一个规则表达式将与后面依次跟有任意数量的空格或制表键以及开始圆括号的字符串 "main" 匹配。这应该与一般 ANSI C main() 声明的开始匹配。
在这个特别的规则表达式中,出现了 '[[:space:]]' 字符类。这只是一个特殊的关键字,它告诉 sed 与 TAB 或空格匹配。如果愿意的话,可以不输入 '[[:space:]]',而输入 '[',然后是空格字母,然后是 -V,然后再输入制表键字母和 ']' -- Control-V 告诉 bash 要插入“真正”的制表键,而不是执行命令扩展。使用 '[[:space:]]' 命令类(特别是在脚本中)会更清楚。
好,现在看一下第二个 regexp。'/^}' 将与任何出现在新行行首的 '}' 字符匹配。如果代码的格式很好,那么这将与 main() 函数的结束花括号匹配。如果格式不好,则不会正确匹配 -- 这是执行模式匹配任务的一件棘手之事。
因为是处于 '-n' 安静方式,所以 'p' 命令还是完成其惯有任务,即明确告诉 sed 打印该行。试着对 C 源文件运行该命令 -- 它应该输出整个 main() { } 块,包括开始的 "main()" 和结束的 '}'。
替换!
让我们看一下 sed 最有用的命令之一,替换命令。使用该命令,可以将特定字符串或匹配的规则表达式用另一个字符串替换。下面是该命令最基本用法的示例:
上面的命令将 myfile.txt 中每行第一次出现的 'foo'(如果有的话)用字符串 'bar' 替换,然后将该文件内容输出到标准输出。请注意,我说的是每行第一次出现,尽管这通常不是您想要的。在进行字符串替换时,通常想执行全局替换。也就是说,要替换每行中的所有出现,如下所示:
在最后一个斜杠之后附加的 'g' 选项告诉 sed 执行全局替换。
关于 's///' 替换命令,还有其它几件要了解的事。首先,它是一个命令,并且只是一个命令,在所有上例中都没有指定地址。这意味着,'s///' 还可以与地址一起使用来控制要将命令应用到哪些行,如下所示:
上例将导致用短语 'entrapment' 替换所有出现的短语 'enchantment',但是只在第一到第十行(包括这两行)上这样做。
该例将用 'mountains' 替换 'hills',但是,只从空行开始,到以三个字符 'END' 开始的行结束(包括这两行)的文本块上这样做。
关于 's///' 命令的另一个妙处是 '/' 分隔符有许多替换选项。如果正在执行字符串替换,并且规则表达式或替换字符串中有许多斜杠,则可以通过在 's' 之后指定一个不同的字符来更改分隔符。例如,下例将把所有出现的 /usr/local 替换成 /usr:
在该例中,使用冒号作为分隔符。如果不指定分隔符,则变成了如下:
这样就不能执行了
如果需要在规则表达式中指定分隔符字符,可以在它前面加入反斜杠。
规则表达式混乱
目前为止,我们只执行了简单的字符串替换。虽然这很方便,但是我们还可以匹配规则表达式。例如,以下 sed 命令将匹配从 '<' 开始、到 '>' 结束、并且在其中包含任意数量字符的短语。下例将删除该短语(用空字符串替换):
这是要从文件除去 HTML 标记的第一个很好的 sed 脚本尝试,但是由于规则表达式的特有规则,它不会很好地工作。原因何在?当 sed 试图在行中匹配规则表达式时,它要在行中查找最长的匹配。在我的前一篇 sed 文章中,这不成问题,因为我们使用的是 'd' 和 'p' 命令,这些命令总要删除或打印整行。但是,在使用 's///' 命令时,确实有很大不同,因为规则表达式匹配的整个部分将被目标字符串替换,或者,在本例中,被删除。这意味着,上例将把下行:
幸运的是,有一种简便方法来纠正该问题。我们不输入“'<' 字符后面跟有一些字符并以 '>' 字符结束”的规则表达式,
而只需输入一个“'<' 字符,后面跟有任意数量非 '>' 字符,并以 '>' 字符结束”的规则表达式。这将与最短、而不是最长的可能性匹配。新命令如下:
s e d是一个非交互性文本流编辑器。它编辑文件或标准输入导出的文本拷贝。
引用: |
• 抽取域。 • 匹配正则表达式。 • 比较域。 • 增加、附加、替换。 • 基本的s e d命令和一行脚本。 |
可以在命令行输入s e d命令,也可以在一个文件中写入命令,然后调用s e d,这与a w k基本相同。使用s e d需要记住的一个重要事实是,无论命令是什么, s e d并不与初始化文件打交道,它操作的只是一个拷贝,然后所有的改动如果没有重定向到一个文件,将输出到屏幕。
因为s e d是一个非交互性编辑器,必须通过行号或正则表达式指定要改变的文本行。
本文介绍s e d用法和功能。本章大多编写的是一行命令和小脚本。这样做可以慢慢加深对s e d用法的了解,取得宝贵的经验,以便最终自己编出大的复杂s e d脚本。
和g r e p与a w k一样, s e d是一种重要的文本过滤工具,或者使用一行命令或者使用管道与g r e p与a w k相结合。
1 sed怎样读取数据
s e d从文件的一个文本行或从标准输入的几种格式中读取数据,将之拷贝到一个编辑缓冲区,然后读命令行或脚本的第一条命令,并使用这些命令查找模式或定位行号编辑它。重复此过程直到命令结束。
2 调用sed
调用s e d有三种方式:在命令行键入命令;将s e d命令插入脚本文件,然后调用s e d;将s e d命令插入脚本文件,并使s e d脚本可执行。
使用s e d命令行格式为:
代码: |
sed [选项] s e d命令输入文件。 |
记住在命令行使用s e d命令时,实际命令要加单引号。s e d也允许加双引号。
使用s e d脚本文件,格式为:
代码: |
sed [选项] -f sed脚本文件输入文件 |
要使用第一行具有s e d命令解释器的s e d脚本文件,其格式为:
代码: |
s e d脚本文件[选项] 输入文件 |
不管是使用s h e l l命令行方式或脚本文件方式,如果没有指定输入文件, s e d从标准输入中接受输入,一般是键盘或重定向结果。
引用: |
s e d选项如下: n 不打印;s e d不写编辑行到标准输出,缺省为打印所有行(编辑和未编辑)。p命令可以用来打印编辑行。 c 下一命令是编辑命令。使用多项编辑时加入此选项。如果只用到一条s e d命令,此选项无用,但指定它也没有关系。 f 如果正在调用s e d脚本文件,使用此选项。此选项通知s e d一个脚本文件支持所有的s e d命令,例如:sed -f myscript.sed input_file,这里m y s c r i p t . s e d即为支持s e d命令的文件。 |
2.1 保存sed输出
由于不接触初始化文件,如果想要保存改动内容,简单地将所有输出重定向到一个文件即可。下面的例子重定向s e d命令的所有输出至文件‘ m y o u t f i l e’,当对结果很满意时使用这种方法。
代码: |
$sed 'some-sed-commands' input-file > myoutfile |
2.2 使用sed在文件中查询文本的方式
s e d浏览输入文件时,缺省从第一行开始,有两种方式定位文本:
引用: |
1) 使用行号,可以是一个简单数字,或是一个行号范围。 2 ) 使用正则表达式 |
下面是使用s e d定位文本的一些方式。
代码: |
x x为一行号,如1 x , y 表示行号范围从x到y,如2,5表示从第2行到第5行 / p a t t e r n / 查询包含模式的行。例如/ d i s k /或/[a-z]/ / p a t t e r n / p a t t e r n / 查询包含两个模式的行。例如/ d i s k / d i s k s / p a t t e r n / , x 在给定行号上查询包含模式的行。如/ r i b b o n / , 3 x , / p a t t e r n / 通过行号和模式查询匹配行。3 . / v d u / x , y ! 查询不包含指定行号x和y的行。1 , 2 ! |
2.3 基本sed编辑命令
代码: |
sed编辑命令 p 打印匹配行 = 显示文件行号 a 在定位行号后附加新文本信息 i 在定位行号后插入新文本信息 d 删除定位行 c 用新文本替换定位文本 s 使用替换模式替换相应模式 r 从另一个文件中读文本 w 写文本到一个文件 q 第一个模式匹配完成后推出或立即推出 l 显示与八进制A S C I I代码等价的控制字符 { } 在定位行执行的命令组 n 从另一个文件中读文本下一行,并附加在下一行 g 将模式2粘贴到/pattern n/ y 传送字符 n 延续到下一输入行;允许跨行的模式匹配语句 |
sed和正则表达式
s e d识别任何基本正则表达式和模式及其行匹配规则。记住规则之一是:如果要定位一特殊字符,必须使用( )屏蔽其特殊含义
s e d例子中使用下述文本文件q u o t e . t x t。
代码: |
[sam@Linux_chenwy sam]$ cat quote.txt The honeysuckle band played all night long for only $90. It was an evening of splendid music and company. Too bad the disco floor fell through at 23:00. The local nurse Miss P.Neave was in attendance. |
1 使用p(rint)显示行
只打印第二行,用-n
代码: |
[sam@Linux_chenwy sam]$ sed -n '2p' quote.txt It was an evening of splendid music and company. |
2 打印范围
可以指定行的范围,现打印1到3行,用逗号分隔行号。
代码: |
[sam@Linux_chenwy sam]$ sed -n '1,3p' quote.txt The honeysuckle band played all night long for only $90. It was an evening of splendid music and company. Too bad the disco floor fell through at 23:00. |
3 打印模式
假定要匹配单词N e a v e,并打印此行,方法如下。使用模式/ p a t t e r n /格式,这里为/ N e a v e /。
代码: |
[sam@Linux_chenwy sam]$ sed -n '/The/p' quote.txt The honeysuckle band played all night long for only $90. The local nurse Miss P.Neave was in attendance. |
4 使用模式和行号进行查询
可以将行号和模式结合使用。假定要改动文件q u o t e . t x t最后一行中的单词t h e,使用s e d查询t h e,返回两行:
代码: |
[sam@Linux_chenwy sam]$ sed -n '/The/p' quote.txt The honeysuckle band played all night long for only $90. The local nurse Miss P.Neave was in attendance. |
使用模式与行号的混合方式可以剔除第一行,格式为l i n e _ n u m b e r, / p a t t e r n /。逗号用来分隔行号与模式开始部分。为达到预期结果,使用4 , / t h e /。意即只在第四行查询模式t h e,命令如下:
代码: |
[sam@Linux_chenwy sam]$ sed -n '4,/The/p' quote.txt The local nurse Miss P.Neave was in attendance. |
上面有错,其实是把第四行后的都打出来了
这个模式应该哪果指定行找不到符合条件的,就从下一行开始查找,直到找到为止,并把,找到行之前的全部打打印出来。
如果指定行本身就符合条伯,把本行及后面的行的全部打印出来
5 匹配元字符
匹配元字符$前,必须使用反斜线屏蔽其特殊含义。模式为/$/ p。
代码: |
[sam@Linux_chenwy sam]$ sed -n '/$/p' quote.txt The honeysuckle band played all night long for only $90. |
6 显示整个文件
要打印整个文件,只需将行范围设为第一行到最后一行1 , $。$意为最后一行。
代码: |
[sam@Linux_chenwy sam]$ sed -n '1,$p' quote.txt The honeysuckle band played all night long for only $90. It was an evening of splendid music and company. Too bad the disco floor fell through at 23:00. The local nurse Miss P.Neave was in attendance. |
7 任意字符
匹配任意字母,后跟任意字母的0次或多次重复,并以i n g结尾,模式为/ . * i n g /。可以使用这个模式查询以i n g结尾的任意单词。
代码: |
[sam@Linux_chenwy sam]$ sed -n '/.*ing/p' quote.txt It was an evening of splendid music and company. |
8 首行
要打印文件第一行,使用行号:
代码: |
[sam@Linux_chenwy sam]$ sed -n '1p' quote.txt The honeysuckle band played all night long for only $90. |
9 最后一行
要打印最后一行,使用$。$是代表最后一行的元字符。
代码: |
[sam@Linux_chenwy sam]$ sed -n '$p' quote.txt The local nurse Miss P.Neave was in attendance. |
10 打印行号
要打印行号,使用等号=。打印模式匹配的行号,使用格式/ p a t t e r n / =。
代码: |
[sam@Linux_chenwy sam]$ sed -e '/music/=' quote.txt The honeysuckle band played all night long for only $90. 2 It was an evening of splendid music and company. Too bad the disco floor fell through at 23:00. The local nurse Miss P.Neave was in attendance. |
整个文件都打印出来,并且匹配行打印了行号。如果只关心实际行号,使用- e选项。
代码: |
[sam@Linux_chenwy sam]$ sed -n '/music/=' quote.txt 2 |
如果只打印行号及匹配行,必须使用两个s e d命令,并使用e选项。第一个命令打印模式匹配行,第二个使用=选项打印行号,格式为sed -n -e /pattern/p -e /pattern/=
代码: |
[sam@Linux_chenwy sam]$ sed -n -e '/music/p' -e '/music/=' quote.txt It was an evening of splendid music and company. 2 |
11 附加文本
要附加文本,使用符号a ,可以将指定文本一行或多行附加到指定行。如果不指定文本放置位置, s e d缺省放在每一行后面。附加文本时不能指定范围,只允许一个地址模式。文本附加操作时,结果输出在标准输出上。注意它不能被编辑,因为s e d执行时,首先将文件的一行文本拷贝至缓冲区,在这里s e d编辑命令执行所有操作(不是在初始文件上),因为文本直接输出到标准输出,s e d并无拷贝。
要想在附加操作后编辑文本,必须保存文件,然后运行另一个s e d命令编辑它。这时文件的内容又被移至缓冲区。
附加操作格式如下:
代码: |
[address]a text text ...... text |
地址指定一个模式或行号,定位新文本附加位置。a 通知s e d对a 后的文本进行实际附加操作。观察格式,注意每一行后面有一斜划线,这个斜划线代表换行。s e d执行到这儿,将创建一新行,然后插入下一文本行。最后一行不加斜划线, s e d假定这是附加命令结尾。
当附加或插入文本或键入几个s e d命令时,可以利用辅助的s h e l l提示符以输入多行命令。当附加或插入文本或键入几个s e d命令时,可以利用辅助的s h e l l提示符以输入多行命令。
创建sed脚本文件
创建脚本文件a p p e n d . s e d:
第一行是s e d命令解释行。脚本在这一行查找s e d以运行命令,这里定位在/ b i n。
第二行以/ c o m p a n y /开始,这是附加操作起始位置。a 通知s e d这是一个附加操作,首先应插入一个新行。
第三行是附加操作要加入到拷贝的实际文本。
输出显示附加结果。如果要保存输出,重定向到一个文件。
代码: |
[sam@chenwy sam]$ cat append.sed #!/bin/sed -f /company/ a Then suddenly it happed. |
保存它,增加可执行权限,运行
代码: |
[sam@chenwy sam]chmod u+x append.sed [sam@chenwy sam]$ ./append.sed quote.txt The honeysuckle band played all night long for only $90. It was an evening of splendid music and company. Then suddenly it happed. Too bad the disco floor fell through at 23:00. The local nurse Miss P.Neave was in attendance. |
或直接用命令行:
代码: |
[sam@chenwy sam]$ sed "/company/aThen suddenly it happened." quote.txt The honeysuckle band played all night long for only $90. It was an evening of splendid music and company. Then suddenly it happened. Too bad the disco floor fell through at 23:00. The local nurse Miss P.Neave was in attendance. |
[sam@chenwy sam]$ sed "/company/iutter confusion followed." quote.txt
The honeysuckle band played all night long for only $90.
utter confusion followed.
It was an evening of splendid music and company.
Too bad the disco floor fell through at 23:00.
The local nurse Miss P.Neave was in attendance.
插入文本:
插入命令类似于附加命令,只是在指定行前面插入。和附加命令一样,它也只接受一个地址。
如在a t t e n d a n c e结尾的行前插入文本utter confusion followed。
代码: |
[sam@chenwy sam]$ sed "/company/iUtter confusion followed." quote.txt |
也可以指定行:
代码: |
[sam@chenwy sam]$ cat insert.sed #!/bin/sed -f 4 i Utter confusion followed. |
执行结果
代码: |
[sam@chenwy sam]$ chmod u+x insert.sed [sam@chenwy sam]$ ./insert.sed quote.txt The honeysuckle band played all night long for only $90. It was an evening of splendid music and company. Too bad the disco floor fell through at 23:00. Utter confusion followed. The local nurse Miss P.Neave was in attendance. |
修改文本
修改命令将在匹配模式空间的指定行用新文本加以替代,格式如下:
将第一行The honeysuckle band played all night long for only $90替换为The office Di b b l e band played well。首先要匹配第一行的任何部分,可使用模式‘ / H o n e y s u c k l e /’。s e d脚本文件为c h a n g e . s e d。内容如下:
代码: |
[sam@chenwy sam]$ cat change.sed #!/bin/sed -f 3 c The office Dibble band played well. |
代码: |
[sam@chenwy sam]$ chmod u+x change.sed [sam@chenwy sam]$ ./change.sed quote.txt The honeysuckle band played all night long for only $90. It was an evening of splendid music and company. The office Dibble band played well. The local nurse Miss P.Neave was in attendance. |
或命令行:
代码: |
[sam@chenwy sam]$ sed "/honeysuck/cThe Office Dibble band played well." quote.txt The Office Dibble band played well. It was an evening of splendid music and company. Too bad the disco floor fell through at 23:00. The local nurse Miss P.Neave was in attendance. |
可以对同一个脚本中的相同文件进行修改、附加、插入三种动作匹配和混合操作。
代码: |
[sam@chenwy sam]$ cat mix.sed #!/bin/sed -f 1 c The Dibble band were grooving. /evening/ i They played some great tunes. 3 a Where was the nurse to help? |
代码: |
[sam@chenwy sam]$ chmod u+x mix.sed [sam@chenwy sam]$ ./mix.sed quote.txt The Dibble band were grooving. They played some great tunes. It was an evening of splendid music and company. Too bad the disco floor fell through at 23:00. Where was the nurse to help? The local nurse Miss P.Neave was in attendance. |
删除文本
s e d删除文本格式:
代码: |
[ a d d r e s s [,a d d r e s s ] ] d |
删除第一行;1 d意为删除第一行。
代码: |
[sam@chenwy sam]$ sed '1d' quote.txt It was an evening of splendid music and company. Too bad the disco floor fell through at 23:00. The local nurse Miss P.Neave was in attendance. |
删除第一到第三行:
代码: |
[sam@chenwy sam]$ sed '1,3d' quote.txt The local nurse Miss P.Neave was in attendance. |
删除最后一行:
代码: |
[sam@chenwy sam]$ sed '$d' quote.txt The honeysuckle band played all night long for only $90. It was an evening of splendid music and company. Too bad the disco floor fell through at 23:00. |
也可以使用正则表达式进行删除操作。下面的例子删除包含文本‘ N e a v e’的行。
代码: |
[sam@chenwy sam]$ sed '/Neave/d' quote.txt The honeysuckle band played all night long for only $90. It was an evening of splendid music and company. Too bad the disco floor fell through at 23:00. |
替换文本
替换命令用替换模式替换指定模式,格式为:
代码: |
[ a d d r e s s [,address]] s/ pattern-to-find /replacement-pattern/[g p w n] |
s选项通知s e d这是一个替换操作,并查询p a t t e r n - t o - f i n d,成功后用r e p l a c e m e n t - p a t t e r n替换它。
替换选项如下:
引用: |
g 缺省情况下只替换第一次出现模式,使用g选项替换全局所有出现模式。 p 缺省s e d将所有被替换行写入标准输出,加p选项将使- n选项无效。- n选项不打印输出结果。 w 文件名使用此选项将输出定向到一个文件。 |
如替换n i g h t为N I G H T,首先查询模式n i g h t,然后用文本N I G H T替换它。
代码: |
[sam@chenwy sam]$ sed 's/night/NIGHT/' quote.txt The honeysuckle band played all NIGHT long for only $90. It was an evening of splendid music and company. Too bad the disco floor fell through at 23:00. The local nurse Miss P.Neave was in attendance. |
要从$ 9 0 中删除$ 符号(记住这是一个特殊符号,必须用 屏蔽其特殊含义),在r e p l a c e m e n t - p a t t e r n部分不写任何东西,保留空白,但仍需要用斜线括起来。在s e d中也可以这样删除一个字符串。
代码: |
[sam@chenwy sam]$ sed 's/$//' quote.txt The honeysuckle band played all night long for only 90. It was an evening of splendid music and company. Too bad the disco floor fell through at 23:00. The local nurse Miss P.Neave was in attendance. |
要进行全局替换,即替换所有出现模式,只需在命令后加g选项。下面的例子将所有T h e替换成Wo w!。
代码: |
[sam@chenwy sam]$ sed 's/The/Wow!/g' quote.txt Wow! honeysuckle band played all night long for only $90. It was an evening of splendid music and company. Too bad the disco floor fell through at 23:00. Wow! local nurse Miss P.Neave was in attendance. |
将替换结果写入一个文件用w选项,下面的例子将s p l e n d i d替换为S P L E N D I D的替换结果写入文件s e d . o u t:
代码: |
[sam@chenwy sam]$ sed 's/splendid/SPLENDID/w sed.out' quote.txt The honeysuckle band played all night long for only $90. It was an evening of SPLENDID music and company. Too bad the disco floor fell through at 23:00. The local nurse Miss P.Neave was in attendance. |
注意要将文件名括在s e d的单引号里。文件结果如下:
代码: |
[sam@chenwy sam]$ cat sed.out It was an evening of SPLENDID music and company. |
使用替换修改字符串
如果要附加或修改一个字符串,可以使用(&)命令,&命令保存发现模式以便重新调用它,然后把它放在替换字符串里面。
先给出一个被替换模式,然后是一个准备附加在第一个模式后的另一个模式,并且后面带有&,这样修改模式将放在匹配模式之前。
例如, s e d语句s/nurse/"Hello"&/p 的结果如下
代码: |
[sam@chenwy sam]$ sed -n 's/nurse/"hello" &/p' quote.txt The local "hello" nurse Miss P.Neave was in attendance. |
原句是文本行The local nurse Miss P.Neave was in attendance。
记住模式中要使用空格,因为输出结果表明应加入空格。
还有一个例子:
代码: |
[sam@chenwy sam]$ sed -n 's/played/from Hockering &/p' quote.txt The honeysuckle band from Hockering played all night long for only $90. |
原句是The honeysuckle band played all night long for only $90。
将sed结果写入文件命令
像使用>文件重定向发送输出到一个文件一样,在s e d命令中也可以将结果输入文件。格式有点像使用替换命令:
代码: |
[ a d d r e s s [,address]]w filename |
‘w’选项通知s e d将结果写入文件。f i l e n a m e是自解释文件名。
下面有两个例子。
代码: |
[sam@chenwy sam]$ sed '1,2 w filedt' quote.txt The honeysuckle band played all night long for only $90. It was an evening of splendid music and company. Too bad the disco floor fell through at 23:00. The local nurse Miss P.Neave was in attendance. |
文件q u o t e . t x t输出到屏幕。模式范围即1,2行输出到文件f i l e d t。
代码: |
[sam@chenwy sam]$ cat filedt The honeysuckle band played all night long for only $90. It was an evening of splendid music and company. |
下面例子中查询模式N e a v e,匹配结果行写入文件f i l e d h t。
代码: |
[sam@chenwy sam]$ sed '/Neave/ w dht' quote.txt The honeysuckle band played all night long for only $90. It was an evening of splendid music and company. Too bad the disco floor fell through at 23:00. The local nurse Miss P.Neave was in attendance. |
代码: |
[sam@chenwy sam]$ cat dht The local nurse Miss P.Neave was in attendance. |
从文件中读文本
处理文件时, s e d允许从另一个文件中读文本,并将其文本附加在当前文件。此命令放在模式匹配行后,格式为:
代码: |
address r filename |
这里r通知s e d将从另一个文件源中读文本。f i l e n a m e是其文件名。
现在创建一个小文件s e d e x . t x t,内容如下:
代码: |
[sam@chenwy sam]$ echo "Boom boom went the music" >sedex.txt [sam@chenwy sam]$ cat sedex.txt Boom boom went the music |
将s e d e x . t x t内容附加到文件q u o t e . t x t的拷贝。在模式匹配行/ c o m p a n y /后放置附加文本。本例为第三行。注意所读的文件名需要用单引号括起来。
代码: |
[sam@chenwy sam]$ sed '/company./r sedex.txt' quote.txt The honeysuckle band played all night long for only $90. It was an evening of splendid music and company. Boom boom went the music Too bad the disco floor fell through at 23:00. The local nurse Miss P.Neave was in attendance. |
匹配后退出
有时需要在模式匹配首次出现后退出s e d,以便执行其他处理脚本。退出命令格式为:
代码: |
address q |
下面的例子假定查询模式/ . a . * /,意为任意字符后跟字符a,再跟任意字符0次或任意多次。
查询首次出现模式,然后退出。需要将q放在s e d语句末尾。
代码: |
[sam@chenwy sam]$ sed '/.a.*/q' quote.txt The honeysuckle band played all night long for only $90. |
显示文件中的控制字符
1、$vi dos.txt
进入vi后,用ctrl+v 再用ctrl+M产生控制字符^M不知对不对
使用cat -v filename命令查看编辑好的文件
代码: |
[sam@chenwy sam]$ cat -v dos.txt 12332##DISO##45.12^M 00332##LPSO##23.14^M 01299##USPD##34.46^M |
s e d格式为:
代码: |
[ a d d r e s s,[ a d d r e s s ] ] l |
‘l’意为列表。一般情况下要列出整个文件,而不是模式匹配行,因此使用l要从第一到最后一行。模式范围1,$即为此意。
代码: |
[sam@chenwy sam]$ sed -n '1,$l' dos.txt 12332##DISO##45.12 $ 00332##LPSO##23.14 $ 01299##USPD##34.46 $ |
处理控制字符
使用s e d实现的一个重要功能是在另一个系统中下载的文件中剔除控制字符。
下面是传送过来的文件( d o s . t x t)的部分脚本。必须去除所有可疑字符,以便于帐号所有者使用文件。
删除所有的#字符很容易,可以使用全局替换命令。这里用一个空格替换两个或
更多的#符号。
代码: |
[sam@chenwy sam]$ sed 's/##/ /g' dos.txt 12332 DISO 45.12 00332 LPSO 23.14 01299 USPD 34.46 |
。删除所有行首的0。使用^符号表示模式从行首开始, ^ 0 *表示行首任意个0。模式s / ^ 0 * / / g设置替换部分为空,即为删除模式,正是要求所在。
代码: |
[sam@chenwy sam]$ sed 's/##/ /g;s/^0*/ /g' dos.txt 12332 DISO 45.12 332 LPSO 23.14 1299 USPD 34.46 |
最后去除行尾^ M符号,为此需做全局替换。设置替换部分为空。模式为:
‘s / ^ m / / g’,注意‘^ M’,这是一个控制字符。
在命令行里也必须用^M控制字符耶!?
代码: |
[sam@chenwy sam]$ sed 's/##/ /g;s/^0*/ /g;s/^M/ /g' dos.txt 12332 DISO 45.12 332 LPSO 23.14 1299 USPD 34.46 |
或
代码: |
[sam@chenwy sam]$ cat dos.txt | sed 's/^0*/ /g' | sed 's/^M/ /g' | sed 's/##/ /g' |
处理报文输出
当从数据库中执行语句输出时,一旦有了输出结果,脚本即可做进一步处理。通常先做一些整理,下面是一个s q l查询结果。
代码: |
[sam@chenwy sam]$ cat data.txt Database Size(MB) DataCreated ----------------------------- GOSOUTH 2244 12/11/97 TRISUD 5632 8/9/99 (2 rows affected) |
为了使用上述输出信息做进一步自动处理,需要知道所存数据库名称,为此需执行以下
操作:
1) 使用s / - * / / g删除横线- - - - - -。
2) 使用/ ^ $ / d删除空行。
3) 使用$ d删除最后一行
4) 使用1 d删除第一行。
5) 使用awk {print $1}打印第一列。
命令如下,这里使用了c a t,并管道传送结果到s e d命令。
代码: |
[sam@chenwy sam]$ cat data.txt |sed 's/--*/ /g' | sed '/^$/d' | sed '$d' | sed '1d' | awk '{print $1}' GOSOUTH TRISUD |
附加文本
当帐户完成设置一个文件时,帐号管理者可能要在文件中每个帐号后面加一段文字,下面是此类文件的一部分:
代码: |
[sam@chenwy sam]$ cat ok.txt AC456 AC492169 AC9967 AC88345 |
任务是在每一行末尾加一个字符串‘ p a s s e d’。
使用$命令修改各域会使工作相对容易些。首先需要匹配至少两个或更多的数字重复出现,这样将所有的帐号加进匹配模式。
代码: |
[sam@chenwy sam]$ sed 's/[0-9][0-9]*/& Passed/g' ok.txt AC456 Passed AC492169 Passed AC9967 Passed AC88345 Passed |
从shell向sed传值
要从命令行中向s e d传值,值得注意的是用双引号,否则功能不执行。
代码: |
[sam@chenwy sam]$ NAME="It's a go situation" [sam@chenwy sam]$ REPLACE="GO" [sam@chenwy sam]$ echo $NAME | sed "s/go/$REPLACE/g" It's a GO situation |
从sed输出中设置shell变量
从s e d输出中设置s h e l l变量是一个简单的替换过程。运用上面的例子,创建s h e l l变量N E W- N A M E,保存上述s e d例子的输出结果。
代码: |
[sam@chenwy sam]$ NAME="It's a go situation" [sam@chenwy sam]$ REPLACE="GO" [sam@chenwy sam]$ NEW_NAME=`echo $NAME | sed "s/go/$REPLACE/g"` [sam@chenwy sam]$ echo $NEW_NAME It's a GO situation |
这里的`是键盘左上角那个`
下面是一些一行命令集。([ ]表示空格, [ ]表示t a b键)
引用: |
‘s / . $ / / g’ 删除以句点结尾行 ‘-e /abcd/d’ 删除包含a b c d的行 ‘s / [ ] [ ] [ ] * / [ ] / g’ 删除一个以上空格,用一个空格代替 ‘s / ^ [ ] [ ] * / / g’ 删除行首空格 ‘s / . [ ] [ ] * / [ ] / g’ 删除句点后跟两个或更多空格,代之以一个空格 ‘/ ^ $ / d’ 删除空行 ‘s / ^ . / / g’ 删除第一个字符 ‘s /CO L ( . . . ) / / g’ 删除紧跟C O L的后三个字母 ‘s / ^ / / / g’ 从路径中删除第一个 ‘s / [ ] / [ ] / / g’ 删除所有空格并用t a b键替代 ‘S / ^ [ ] / / g’ 删除行首所有t a b键 ‘s / [ ] * / / g’ 删除所有t a b键 |
1. 删除路径名第一个符号
将当前工作目录返回给s e d,删除第一个:
代码: |
[sam@chenwy sam]$ echo $PWD |sed 's/^///g' usr/sam |
2. 追加/插入文本
将"Mr Wi l l i s "字串返回给s e d并在M r后而追加" B r u c e "。
代码: |
[sam@chenwy sam]$ echo "Mr Willis" |sed 's/Mr /& Bruce/g' Mr BruceWillis |
3. 删除首字符
s e d删除字符串“a c c o u n t s . d o c”首字符。
代码: |
[sam@chenwy sam]$ echo "accounts.doc" |sed 's/^.//g' ccounts.doc |
4. 删除文件扩展名
s e d删除“a c c o u n t s . d o c”文件扩展名。
代码: |
[sam@chenwy sam]$ echo "accounts.doc"|sed 's/.doc//g' accounts |
5. 增加文件扩展名
s e d附加字符串“. d o c”到字符串“a c c o u n t s”。
代码: |
[sam@chenwy sam]$ echo "accounts"|sed 's/$/.doc/g' accounts.doc |
6. 替换字符系列
如果变量x含有下列字符串:
代码: |
[sam@chenwy sam]$ x="Department+playroll&Building G" [sam@chenwy sam]$ echo $x Department+playroll&Building G |
如果要加入of,located,并去掉+,&实现下列转换:
代码: |
[sam@chenwy sam]$ echo $x |sed 's/+/ of /g' |sed 's/&/ Located at /g' Department of playroll Located at Building G |
把+用 of 替换,&用located at 替换
挑选编辑器
在 UNIX 世界中有很多文本编辑器可供我们选择。思考一下 -- vi、emacs 和 jed 以及很多其它工具都会浮现在脑海中。我们都有自己已逐渐了解并且喜爱的编辑器(以及我们喜爱的组合键)。有了可信赖的编辑器,我们可以轻松处理任何数量与 UNIX 有关的管理或编程任务。
虽然交互式编辑器很棒,但却有其限制。尽管其交互式特性可以成为强项,但也有其不足之处。考虑一下需要对一组文件执行类似更改的情形。您可能会本能地运行自己所喜爱的编辑器,然后手工执行一组烦琐、重复和耗时的编辑任务。然而,有一种更好的方法。
进入 sed
如果可以使编辑文件的过程自动化,以便用“批处理”方式编辑文件,甚至编写可以对现有文件进行复杂更改的脚本,那将太好了。幸运的是,对于这种情况,有一种更好的方法 -- 这种更好的方法称为 "sed"。
sed 是一种几乎包括在所有 UNIX 平台(包括 Linux)的轻量级流编辑器。sed 有许多很好的特性。首先,它相当小巧,通常要比您所喜爱的脚本语言小很多倍。其次,因为 sed 是一种流编辑器,所以,它可以对从如管道这样的标准输入接收的数据进行编辑。因此,无需将要编辑的数据存储在磁盘上的文件中。因为可以轻易将数据管道输出到 sed,所以,将 sed 用作强大的 shell 脚本中长而复杂的管道很容易。试一下用您所喜爱的编辑器去那样做。
GNU sed
对 Linux 用户来说幸运的是,最好的 sed 版本之一恰好是 GNU sed,其当前版本是 3.02。每一个 Linux 发行版都有(或至少应该有)GNU sed。GNU sed 之所以流行不仅因为可以自由分发其源代码,还因为它恰巧有许多对 POSIX sed 标准便利、省时的扩展。另外,GNU 没有 sed 早期专门版本的很多限制,如行长度限制 -- GNU 可以轻松处理任意长度的行。
最新的 GNU sed
在研究这篇文章之时我注意到:几个在线 sed 爱好者提到 GNU sed 3.02a。奇怪的是,在ftp.gnu.org(有关这些链接,请参阅参考资料)上找不到 sed 3.02a,所以,我只得在别处寻找。我在alpha.gnu.org 的 /pub/sed 中找到了它。于是我高兴地将其下载、编译然后安装,而几分钟后我发现最新的 sed 版本却是 3.02.80 -- 可在alpha.gnu.org 上 3.02a 源代码旁边找到其源代码。安装完 GNU sed 3.02.80 之后,我就完全准备好了。
alpha.gnu.org
alpha.gnu.org(请参阅参考资料)是新的和实验性 GNU 源代码的所在地。然而,您还会在那里发现许多优秀、稳定的源代码。出于某种原因,不是许多 GNU 开发人员忘记将稳定的源代码移至 ftp.gnu.org,就是它们的 "beta" 期间格外长(2 年!)。例如,sed 3.02a 已有两年,甚至 3.02.80 也有一年,但它们仍不能(在 2000 年 8 月写本文章时)在 ftp.gnu.org 上获得。
正确的 sed
在本系列中,将使用 GNU sed 3.02.80。在即将出现的本系列后续文章中,某些(但非常少)最高级的示例将不能在 GNU sed 3.02 或 3.02a 中使用。如果您使用的不是 GNU sed,那么结果可能会不同。现在为什么不花些时间安装 GNU sed 3.02.80 呢?那样,不仅可以为本系列的余下部分作好准备,而且还可以使用可能是目前最好的 sed。
sed 示例
sed 通过对输入数据执行任意数量用户指定的编辑操作(“命令”)来工作。sed 是基于行的,因此按顺序对每一行执行命令。然后,sed 将其结果写入标准输出 (stdout),它不修改任何输入文件。
让我们看一些示例。头几个会有些奇怪,因为我要用它们演示 sed 如何工作,而不是执行任何有用的任务。然而,如果您是 sed 新手,那么理解它们是十分重要的。下面是第一个示例:
代码: |
$ sed -e 'd' /etc/services |
如果输入该命令,将得不到任何输出。那么,发生了什么?
在该例中,用一个编辑命令 'd' 调用 sed。sed 打开 /etc/services 文件,将一行读入其模式缓冲区,执行编辑命令(“删除行”),然后打印模式缓冲区(缓冲区已为空)。然后,它对后面的每一行重复这些步骤。这不会产生输出,因为 "d" 命令除去了模式缓冲区中的每一行!
在该例中,还有几件事要注意。首先,根本没有修改 /etc/services。这还是因为 sed 只读取在命令行指定的文件,将其用作输入 -- 它不试图修改该文件。第二件要注意的事是 sed 是面向行的。'd' 命令不是简单地告诉 sed 一下子删除所有输入数据。相反,sed 逐行将 /etc/services 的每一行读入其称为模式缓冲区的内部缓冲区。一旦将一行读入模式缓冲区,它就执行 'd' 命令,然后打印模式缓冲区的内容(在本例中没有内容)。我将在后面为您演示如何使用地址范围来控制将命令应用到哪些行 -- 但是,如果不使用地址,命令将应用到所有行。
第三件要注意的事是括起 'd' 命令的单引号的用法。养成使用单引号来括起 sed 命令的习惯是个好注意,这样可以禁用 shell 扩展。
另一个 sed 示例
下面是使用 sed 从输出流除去 /etc/services 文件第一行的示例:
代码: |
$ sed -e '1d' /etc/services | more |
地址范围
现在,让我们看一下如何指定地址范围。在本例中,sed 将删除输出的第 1 到 10 行:
代码: |
$ sed -e '1,10d' /etc/services | more |
当用逗号将两个地址分开时,sed 将把后面的命令应用到从第一个地址开始、到第二个地址结束的范围。在本例中,将 'd' 命令应用到第 1 到 10 行(包括这两行)。所有其它行都被忽略。
带规则表达式的地址
现在演示一个更有用的示例。假设要查看 /etc/services 文件的内容,但是对查看其中包括的注释部分不感兴趣。如您所知,可以通过以 '#' 字符开头的行在 /etc/services 文件中放置注释。为了避免注释,我们希望 sed 删除以 '#' 开始的行。以下是具体做法:
代码: |
$ sed -e '/^#/d' /etc/services | more |
让我们分析发生的情况。
要理解 '/^#/d' 命令,首先需要对其剖析。首先,让我们除去 'd' -- 这是我们前面所使用的同一个删除行命令。新增加的是 '/^#/' 部分,它是一种新的规则表达式地址。规则表达式地址总是由斜杠括起。它们指定一种 模式,紧跟在规则表达式地址之后的命令将仅适用于正好与该特定模式匹配的行。
因此,'/^#/' 是一个规则表达式。但是,它做些什么呢?很明显,现在该复习规则表达式了。
规则表达式复习
可以使用规则表达式来表示可能会在文本中发现的模式。您在 shell 命令行中用过 '*' 字符吗?这种用法与规则表达式类似,但并不相同。下面是可以在规则表达式中使用的特殊字符:
引用: |
字符 描述 与行首匹配 与行末尾匹配 与任一个字符匹配 将与前一个字符的零或多个出现匹配 [ ] 与 [ ] 之内的所有字符匹配 |
感受规则表达式的最好方法可能是看几个示例。所有这些示例都将被 sed 作为合法地址接受,这些地址出现在命令的左边。下面是几个示例:
引用: |
规则 表达式 描述 /./ 将与包含至少一个字符的任何行匹配 /../ 将与包含至少两个字符的任何行匹配 /^#/ 将与以 '#' 开始的任何行匹配 /^$/ 将与所有空行匹配 /}^/ 将与以 '}'(无空格)结束的任何行匹配 /} *^/ 将与以 '}' 后面跟有零或多个空格结束的任何行匹配 /[abc]/ 将与包含小写 'a'、'b' 或 'c' 的任何行匹配 /^[abc]/ 将与以 'a'、'b' 或 'c'开始的任何行匹配 |
在这些示例中,鼓励您尝试几个。花一些时间熟悉规则表达式,然后尝试几个自己创建的规则表达式。可以如下使用 /^#/:
代码: |
$ sed -e '/^#/d' /etc/services | more |
这将导致 sed 删除任何匹配的行。删除以#开头的行
另一个例子:
代码: |
$ sed -n -e '/^#/p' /path/to/my/test/file | more |
请注意新的 '-n' 选项,该选项告诉 sed 除非明确要求打印模式空间,否则不这样做。您还会注意到,我们用 'p' 命令替换了 'd' 命令,如您所猜想的那样,这明确要求 sed 打印模式空间。就这样,将只打印匹配部分。打印以#开头的行
有关地址的更多内容
目前为止,我们已经看到了行地址、行范围地址和 ^# 地址。但是,还有更多的可能。我们可以指定两个用逗号分开的规则表达式,sed 将与所有从匹配第一个规则表达式的第一行开始,到匹配第二个规则表达式的行结束(包括该行)的所有行匹配。例如,以下命令将打印从包含 "BEGIN" 的行开始,并且以包含 "END" 的行结束的文本块:
代码: |
$ sed -n -e '/BEGIN/,/^END/p' /my/test/file | more |
如果没发现 "BEGIN",那么将不打印数据。如果发现了 "BEGIN",但是在这之后的所有行中都没发现 "END",那么将打印所有后续行。发生这种情况是因为 sed 面向流的特性 -- 它不知道是否会出现 "END"。
C 源代码示例
如果只要打印 C 源文件中的 main() 函数,可输入:
代码: |
$ sed -n -e '/main[[:space:]]*(/,/^}/p' sourcefile.c | more |
以main后面跟空格或制表键,以(开头,}结尾的
该命令有两个规则表达式 '/main[[:space:]]*(/' 和 '/^}/',以及一个命令 'p'。第一个规则表达式将与后面依次跟有任意数量的空格或制表键以及开始圆括号的字符串 "main" 匹配。这应该与一般 ANSI C main() 声明的开始匹配。
在这个特别的规则表达式中,出现了 '[[:space:]]' 字符类。这只是一个特殊的关键字,它告诉 sed 与 TAB 或空格匹配。如果愿意的话,可以不输入 '[[:space:]]',而输入 '[',然后是空格字母,然后是 -V,然后再输入制表键字母和 ']' -- Control-V 告诉 bash 要插入“真正”的制表键,而不是执行命令扩展。使用 '[[:space:]]' 命令类(特别是在脚本中)会更清楚。
好,现在看一下第二个 regexp。'/^}' 将与任何出现在新行行首的 '}' 字符匹配。如果代码的格式很好,那么这将与 main() 函数的结束花括号匹配。如果格式不好,则不会正确匹配 -- 这是执行模式匹配任务的一件棘手之事。
因为是处于 '-n' 安静方式,所以 'p' 命令还是完成其惯有任务,即明确告诉 sed 打印该行。试着对 C 源文件运行该命令 -- 它应该输出整个 main() { } 块,包括开始的 "main()" 和结束的 '}'。
替换!
让我们看一下 sed 最有用的命令之一,替换命令。使用该命令,可以将特定字符串或匹配的规则表达式用另一个字符串替换。下面是该命令最基本用法的示例:
代码: |
$ sed -e 's/foo/bar/' myfile.txt |
上面的命令将 myfile.txt 中每行第一次出现的 'foo'(如果有的话)用字符串 'bar' 替换,然后将该文件内容输出到标准输出。请注意,我说的是每行第一次出现,尽管这通常不是您想要的。在进行字符串替换时,通常想执行全局替换。也就是说,要替换每行中的所有出现,如下所示:
代码: |
$ sed -e 's/foo/bar/g' myfile.txt |
在最后一个斜杠之后附加的 'g' 选项告诉 sed 执行全局替换。
关于 's///' 替换命令,还有其它几件要了解的事。首先,它是一个命令,并且只是一个命令,在所有上例中都没有指定地址。这意味着,'s///' 还可以与地址一起使用来控制要将命令应用到哪些行,如下所示:
代码: |
$ sed -e '1,10s/enchantment/entrapment/g' myfile2.txt |
上例将导致用短语 'entrapment' 替换所有出现的短语 'enchantment',但是只在第一到第十行(包括这两行)上这样做。
代码: |
$ sed -e '/^$/,/^END/s/hills/mountains/g' myfile3.txt |
该例将用 'mountains' 替换 'hills',但是,只从空行开始,到以三个字符 'END' 开始的行结束(包括这两行)的文本块上这样做。
关于 's///' 命令的另一个妙处是 '/' 分隔符有许多替换选项。如果正在执行字符串替换,并且规则表达式或替换字符串中有许多斜杠,则可以通过在 's' 之后指定一个不同的字符来更改分隔符。例如,下例将把所有出现的 /usr/local 替换成 /usr:
代码: |
$ sed -e 's:/usr/local:/usr:g' mylist.txt |
在该例中,使用冒号作为分隔符。如果不指定分隔符,则变成了如下:
代码: |
$ sed -e 's/usr/local/usrg' mylist.txt |
这样就不能执行了
如果需要在规则表达式中指定分隔符字符,可以在它前面加入反斜杠。
规则表达式混乱
目前为止,我们只执行了简单的字符串替换。虽然这很方便,但是我们还可以匹配规则表达式。例如,以下 sed 命令将匹配从 '<' 开始、到 '>' 结束、并且在其中包含任意数量字符的短语。下例将删除该短语(用空字符串替换):
代码: |
$ sed -e 's/<.*>//g' myfile.html |
这是要从文件除去 HTML 标记的第一个很好的 sed 脚本尝试,但是由于规则表达式的特有规则,它不会很好地工作。原因何在?当 sed 试图在行中匹配规则表达式时,它要在行中查找最长的匹配。在我的前一篇 sed 文章中,这不成问题,因为我们使用的是 'd' 和 'p' 命令,这些命令总要删除或打印整行。但是,在使用 's///' 命令时,确实有很大不同,因为规则表达式匹配的整个部分将被目标字符串替换,或者,在本例中,被删除。这意味着,上例将把下行:
代码: |
<b>This</b> is what <b>I</b> meant. 变成: meant. 我们要的不是这个,而是: This is what I meant. |
幸运的是,有一种简便方法来纠正该问题。我们不输入“'<' 字符后面跟有一些字符并以 '>' 字符结束”的规则表达式,
而只需输入一个“'<' 字符,后面跟有任意数量非 '>' 字符,并以 '>' 字符结束”的规则表达式。这将与最短、而不是最长的可能性匹配。新命令如下:
代码: |
相关阅读 更多 +