注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Koala++'s blog

计算广告学 RTB

 
 
 

日志

 
 

Sed 介绍和教程[1]  

2010-12-29 06:10:22|  分类: shell |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

Sed 介绍和教程

作者:Bruce Barnett

译者:Koala++

 

原文地址:http://www.grymoire.com/Unix/Sed.html

注:译者不懂sed

Sed 介绍

      如果你想写一个程序对一个文件做一些改动,那就sed就是你应该使用的工具。这篇文章就是教你如何使用这个自动修改文件的特殊编辑器。

      Unix工具中有一些程序是真正的重型武器。这些程序在简单的应该中很容易使用,但是还有大量的命令可以进行复杂的操作。不要因为惧怕它强大能力但复杂指令,而使你放弃使用它的简单特性,这章,如果其它章一些,以介绍一些简单的概念为开始,然后介绍一些高级的话题。一个关于注释的注意事项,当我开始写这篇文章时,大多数版本的sed不允许你将注释放到脚本里,以’#’字符开始的行为注释。新的版本的sed也许会支持行尾注释。

关于Sed的可怕事实

Sed是一个终极的流编辑器。如果你感觉流编辑器听起来很奇怪,就想象一下一个通过管道的水流。是的,如果这个水流参管道里,你不可能看到水流。这就是我拿水流比喻的原因。

不管怎么说,sed是一个非常有用的工具,不幸的是,大多数人没见识过它的真正的力量。这个语言非常简单,但文档糟糕透了。Solais上关于sed的在线文档有5页长,其中2页描述了34种你可能会看到的错误,一个记录错误的文档长度和描述这个语言的文档长度相当的语言,你学习它时会有着很陡峭的学习曲线。

别发愁!不懂sed不是你的错,我将在下面介绍sed的方方面面,但我介绍sed的那些特性时,我会以我学习它们的顺序来介绍,我并不是一下把它们全学会了,你也不需要一下全学会。

非常重要的命令:做替换的s

Sed有几个命令,但大多数人只学了一个替换命令:s。替换命令替换所有匹配正则表达式的值为一个新值,一个简单的例子,它将在”old”文件中的”day”替换为”night”后存入”new”文件中。

sed s/day/night/ <old >new

或是另一种方式(对于Unix新手来说)

sed s/day/night/ <old >new

如果你想测试一下功能,你可以用这个命令:

echo day | sed s/day/night/

这将会输入”night”

我没有给参数加引号是因为在这个例子中不需要加,但是我建议你使和引号。如果你在命令中有元字符,引号是必要的,我了为强调这是一个好的习惯,我会在下面的示例中都加上单引号,如果加上引号,那么上例就是:

sed 's/day/night/' <old >new

我必须强调的是sed编辑器会严格执行你的命令,如果你执行

echo Sunday | sed 's/day/night/'

这将会输出”Sunnight”因为它发现输入有”day”这个字符串。

替换命令有四个组成部分:

s          替换命令

/../../    分隔符

day        正则表达式模式,查找模式

night      替换字符串

查找模式在左边,替换字符串在右边。

我们已经学习了引用和正则表达式。那是学习替换命令90%的工作量。换句话说,你已经知道90%最常用的sed使用方法,但仍有一些未来的sed高手需要了解的内容(你已经看了第1节,还有63节就看完了。),噢。……,如果你没看完,那么你就收藏这个页面吧。

&作为匹配的串

有时你想查找一个模式,然后加上几个字符,比如在匹配的串前后加上括号。如果你是找一个确定的字符串,还是比较简单的,如下即可:

sed ‘s/abc/(abc)/’ < old > new

如果你不是非常清楚你将找到的是串是什么,那你应该如果来利用你找到的串做某种替换呢?

答案就是你需要一个特定的字符”&”,它即指代匹配的模式

sed ‘s/[a-z]*/(&)/’ < old > new

你可以在替换时多次使用”&”,比如,你可以次每行开头的数字复制一次,如下:

% echo “123 abc” | sed ‘s/[0-9]*/& &/’

123 123 abc

让我再修正一下这个例子,sed会以贪婪的方式匹配第一个串。对于’[0-9]*’的第一个区配是第一个字符,因为这个正则是匹配0个或多个数字。所以如果输入是”abc 123”,输出也不会改变(当然,除了有一个空格在字母之前)。一个能保证能匹配一个数字的更好的复制数字的方法是:

% echo “123 abc” | sed ‘s/[0-9][0-9]*/& &/’

123 123 abc

字符串”abc”没有改变,因为它没有匹配正则表达式,如果你想在输出中删除”abc”,你必须扩展正则表达式来匹配行的其它的部分,并显式地用”(”,”)””\1”来指名,这就是下节的内容了。

\1来指明匹配的部分

我已经在正则表达式那一章中介绍了”(” ”)””\1”的用法。现在复习一下,被转义的括号(即,有反斜杠在前面的括号)来记录正则表达的某一部分。”\1”是被记录的第一个模式,”\2”是第二个被记录的模式。Sed可以记录9个模式。

如果你想保存每行的第一个单词,并删除行的其它部分,你可以用下面的命令:

sed ‘s/\(\[a-z]*).*/\1/’

我应该再详细地解释一下。正则表达式是以贪婪的方式匹配。”[a-z]*”匹配0个或多个小写字母,它会尽量匹配更多的小写字母。”.*”会在第一个匹配后匹配0个或多个字符。因为第一个模式已经匹配了所有的小写字母,第二个模式会匹配剩下的字符,所以你使用命令:

echo abcd123 | sed ‘s/\([a-z]*\).*/\1/’

会输出”abcd”,而删除后面的数字。

如果你想调换两个单词的位置,你可记录两个模式,并改变它们的次序。

sed ‘s/\([a-z]*\) \([a-z]*\)/\2 \1/’

注意两个模式中间是有空格的。这个可保证找到两个单词。但是[a-z]*这种模式会匹配0个字母,如果你想至少匹配一个字母,你可以用下面的命令:

sed ‘s/\([a-z][a-z]*\) \([a-z][a-z]*\)/\2 \1/’

“\1”并不需要一定出现在替换串中(右部),它你也可以在查找的模式中(左部)。如果你想删除重复的单词,你可以用:

sed ‘s/\([a-z]*\) \1/\1/’

注意你最多使用9个值,从”\1””\9”

替换标志

你可以在最后一个分隔符后加上标志,这些标志可以指明当当有多个模式在一行中被匹配,应该如何替换,让我们来看看它们吧。

/g 全局替换

大多数Unix工具是以每次读一行的方式对文件进行操作,sed,默认也是以行为单位。如果你告诉它你想改变一个单词,它只会改变每行第一次出现的这个单词,你可能想改变行中的每个单词。比如,我想给一行的每个单词加上括号,如果用[A-Za-z]*这种模式,将不会匹配”won’t”这种单词,我们用另一种模式,”[^ ]”,它会匹配除空格外的一切串。是的,这将会匹配任何串,因为”*”表示0个或多个。现在的sed版本见到这个模式会不太开心,它会产生如”Output line to long”的错误或是一直运行。我将它视为一个bug,并已经报告给Sun公司了,作为一个暂时的解决方法,你在使用”g”标志时必须避免匹配空串。一个变通的方法是”[^ ][^ ]*”,下面的命令会将第一个单词加上括号。

如果你想能每一个单词进行改动,你可以用下面的变通方法:

sed ‘s/[^ ][^ ]*/(&)/g’ < old > new

sed 是递归的吗?

    Sed只在原始的数据中匹配模式,也就是在读取输入的一行时,当一个模式匹配了,就修改的输出就产生了,然后再会去扫描这行的余下的内容。”s”命令不会再去扫描新产生的输出。也就是,你不用去担心下面的这种正则表达式:

Sed ‘s/loop/loop the loop/g’ < old > new

这不会产生死循环。如果执行第二个”s”命令,它会去修改第一个”s”命令的输出结果,我将在下面告诉如何执行多个命令。

/1, /2 等等 指明哪个发生了

    不带标志,只有第一模式会被改变,如果带上”g”标志,所有的模式都会改变。如果你想改变一个特定的模式,但它又不是这行的匹配的第一个模式,你可以用”(””)”来标记每个模式,并且可以用”\1”表示第一个匹配的串,下面的例子是保留的第一个单词,删除第二个单词的命令:

sed ‘s/\([a-zA-Z]*\) \([a-zA-Z]*\) /\1/’ < old > new

    倒。还有一个更容易的方法,你可以在替换命令后加一个数字表明你只想匹配某个特定的模式,比如:

sed ‘s/[a-zA-Z]* //2’ < old > new

    你也可以和”g” (global)标志结合使用,比如,如果你想只保留第一个单词,并把第二个,第三,等等都改为DELETED,使用/2g

sed ‘s/[a-zA-Z]* /DELETED/2g’ < old > new

    不要把/2\2的意义搞混了,/2用在命令的末尾,而\2用在替换部分。

    注意在”*”字符后有一个空格,没有这个空格,sed会运行很长很长时间(注意,这个bug可能已经被修正了),这是因为数字标志和”g”标志有着相同的bug,你可以运行下面的命令:

sed ‘s/[^ ]*//2’ < old > new

    但这会把CPU消耗完,如果你在一台Unix系统上运行下面的例子,它会从密码文件中移除加密后的密码:

sed ‘s/[^:]*//2’ < /etc/passwd > /etc/password.new

    但在我写这个例子时,它还不能正常工作。并且用[^:][:^]也不会解决这个问题,因为它不会匹配一个不存大的密码,所以它会删除第三列,也就是user ID!所以你必须用下面这种丑陋的括号来做。

sed ‘s/^\([^:]*\):[^:]:/\1::/’ < /etc/passwd > /etc/password.new

    你还应该在第一个模式后加一个”:”,这样它就不会匹配空了:

sed  ‘s/[^:]*:/:/2’ < /etc/passwd > /etc/passwd.new

    数字标志不一定非要是一位的,它可以是从1512的任何值。如果你想在每行的80个字符后加一个冒号,你可写:

sed ‘s/./&:/80’ < file > new

    你也可以以蛮力解决,如下:

sed 's/^................................................................................/&:/' <file >new

/p 打印

Sed默认打印每行。如果它做了替换,就会打印新的文本。如果你使用”sed -n”,它就不会打印,默认,会打印所有新产生的行。当使用”-n”选项时,使用”p”标志会打印修改过的串。下面的例子执行的效果与grep相同。

sed –n ‘s/pattern/&/p’ < file

/w filename写入一个文件

在第三个分隔符后还可以跟别的标志,带上”w”标志可以指定一个接收修改数据的文件。下面的例子是将所有为偶数的数且后面为空格的行写入even文件。

sed –n ‘s/^[0-9]*[02468]’ /&/w even’ < file

在这个例子中,输出文件是不需要的,因为输出没有改变。在使用”w”标志时,你必须确信你在”w”和文件名有且只有一个空格。你可以在一个sed命令中打开10个文件,这样你可以将数据流拆分到不同的文件中,使用上例和下面将介绍的多个替换命令,你可以将一个文件根据最后一个数字拆分到十个文件。你还可以用这个方法将错误日志或调试信息写到一个特定文件中。

将替换标志结合

你可以将标志结合起来使用,当然”w”必须是最后一个标志,比如下面的例子:

sed –n ‘s/a/A/2pw /tmp/file’ < old > new

接下来我将介绍sed的选项和调用sed的不同方法。

参数和sed的调用

先前,我只使用了一个替换命令,如果你想做两次替换,并且不想去读sed手册,你可以用管道将多个sed连起来:

sed ‘s/BEGIN/begin/’ < old | sed ‘s/END/end/’ > new

这种写法使用了两个进程,但是sed大师在一个进程可以完成时,决不会用两个。

多个命令使用-e选项

将多个命令结合起来的一种方法是在每个命令前加上-e选项:

sed –e ‘s/a/A/’ –e ‘s/b/B/’ < old > new

在先前的例子中并不需要”-e”选项,因为sed知道至少会有一个命令,如果你给sed一个参数,它必须是一个命令,并且sed处理来自标准输入的数据。

命令行中的文件名(s)

你可以在命令行中指定你想要的文个,如果在没有使用选项的情况下,有多于一个参数传递给sed,它必须是一个文件名,下面的例子是在三个文件中数不以”#”开头的行数。

sed ‘s/^#.*//’ f1 f2 f3 | grep –v ‘^$’ | wc -l

sed替换命令会将以”#”开头的行替换为空行,grep用于过滤空行,wc计算剩余的行数,sed还有别的命令可以不用grep来完成这个功能,我过会再介绍。

当然你可以用”-e”选项来执行上面的例子:

sed –e ‘s/^#.*//’ f1 f2 f3 | grep –v ‘^$’ | wc –l

sed还有两个选项没介绍。

sed –n:不打印

使用“-n”选项后,除非明确指明要打印,否则不打印任何内容。我在前在提到过”/p”标志可以让它又可以打印。让我再讲清楚点,命令:

sed ‘s/PATTERN/&/p’ file

如果PATTERN没在文件中出现,这个命令表现的就和cat一样,即没有内容被改变。如果有PATTERN在文件中出现,那么每行被打印两次。加上”-n”选择,如下面的这个例子就如同grep命令:

sed –n ‘s/PATTERN/&/p’ file

    那它只打印包含PATTERN的行。

sed –f scriptname

如果你有大量的sed命令,你可以把它们放到一个文件中,再用下面命令执行:

sed –f sedcript < old > new

    其中sedscript类似于下面的例子:

# sed comment - This script changes lower case vowels to upper case

s/a/A/g

s/e/E/g

s/i/I/g

s/o/O/g

s/u/U/g

    当有多个命令在一个文件中,每个命令必须是单独的一行。

shell脚本中的sed

    如果你有许多命令,它们不适合写在一行中,你可以用反斜杠将它们分开:

sed -e 's/a/A/g' \

    -e 's/e/E/g' \

    -e 's/i/I/g' \

    -e 's/o/O/g' \

    -e 's/u/U/g'  <old >new

C Shell中引用多行sed

    你可以在C Shell中写入一个大的多行sed脚本,但你必须告诉C shell sed引用跨了几行,这可以通过在每行后加上反斜杠:

#!/bin/csh -f

sed 's/a/A/g  \

s/e/E/g \

s/i/I/g \

s/o/O/g \

s/u/U/g'  <old >new

Bourne shell中引用多行sed

Bourne shell中不用加上斜杠,就可以引用多行:

#!/bin/sh

sed '

s/a/A/g

s/e/E/g

s/i/I/g

s/o/O/g

s/u/U/g'  <old >new

sed 脚本

另一个执行sed的方法是用脚本,创建一个脚本名为CapVowel,并给它执行权限,内容如下:

#!/bin/sed -f

s/a/A/g

s/e/E/g

s/i/I/g

s/o/O/g

s/u/U/g

    你可以用下面的命令执行:

CapVowel < old > new

注释

Sed注释是第一个非空白字符是”#”的行,在许多系统上,sed只能一个注释,并且它必须是脚本的第一行。在Sun(1988年我写这篇文章时),你可以在脚本的任何地方写注释。现代的版本的sed支持这种写法。如果第一行以”#n”开头,那么它和使用”-n”选项的效果相同:默认关闭打印。但这种写法在sed脚本行不通,因为在sed脚本中第一行必须以”#!/bin/sed -f”开头,因为我认为”#!/bin/sed -nf”会产生一个错误。在我2008年尝试这种写法的时候还是不能工作的,因为sed会认为”n”是文件名,但是:

“#!/bin/sed -nf”

却是可以工作的。

将参数传递到sed脚本

如果你还记得my tutorial on the Unix quoting mechanism,将一个词传递到shell脚本,再调用是sed很简单的。复习一下,你可以用单引号可以打开引用或关闭引用。下面是一个很简单的脚本来模拟grep

#!/bin/sh

sed –n ‘s/’$1’/&/p’

但是这个脚本有一个问题,如果你将空格作为参数,这个脚本会引起一个语法错误,一个更好的版本可以防止这种情况的发生。

#!/bin/sh

sed -n 's/'"$1"'/&/p'

将上面的内容保存到sedgrep文件中,你可以键入:

sedgrep '[A-Z][A-Z]' <file

这可以让sed表现地和grep命令一样。

  评论这张
 
阅读(3690)| 评论(2)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017