sed学习实例(一)

在转载陈皓老师的极其简明的《sed简明教程》之上,增加其中未详细涉及的部分,继续对sed庖丁解牛式学习!


1、删除空行
删除前:

[liujl@localhost mysed]$ cat my
This is my cat, my cat's name is betty


This is my dog, my dog's name is frank
This is my fish, my fish's name is george
This is my goat, my goat's name is adam
删除后:
[liujl@localhost mysed]$ sed '/^$/d' my 
This is my cat, my cat's name is betty
This is my dog, my dog's name is frank
This is my fish, my fish's name is george
This is my goat, my goat's name is adam

2、删除行首的空格
[liujl@localhost mysed]$ cat my
  This is my cat, my cat's name is betty


This is my dog, my dog's name is frank
    This is my fish, my fish's name is george
This is my goat, my goat's name is adam
执行命令:
(1)sed 's/^ *//g' my
[liujl@localhost mysed]$ sed 's/^ *//g' my
This is my cat, my cat's name is betty


This is my dog, my dog's name is frank
This is my fish, my fish's name is george
This is my goat, my goat's name is adam
(2)sed 's/^[ ]*//g' my   (注意:[ ] 中有一个空格键)
[liujl@localhost mysed]$ sed 's/^[ ]*//g' my
This is my cat, my cat's name is betty


This is my dog, my dog's name is frank
This is my fish, my fish's name is george
This is my goat, my goat's name is adam
(3)sed 's/^[[:space:]]*//g' my
[liujl@localhost mysed]$ sed 's/^[[:space:]]*//g' my
This is my cat, my cat's name is betty


This is my dog, my dog's name is frank
This is my fish, my fish's name is george
This is my goat, my goat's name is adam
[[:space:]]表示空格或者tab的集合。


3、替换

让我们看一下 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 

在该例中,使用冒号作为分隔符。如果需要在规则表达式中指定分隔符字符,可以在它前面加入反斜杠。


4、字符匹配

字符类

描述

[:alnum:]

字母数字 [a-z A-Z0-9]

[:alpha:]

字母 [a-z A-Z]

[:blank:]

空格或制表键

[:cntrl:]

任何控制字符

[:digit:]

数字 [0-9]

[:graph:]

任何可视字符(无空格)

[:lower:]

小写 [a-z]

[:print:]

非控制字符

[:punct:]

标点字符

[:space:]

空格

[:upper:]

大写 [A-Z]

[:xdigit:]

十六进制数字 [0-9a-f A-F]


5、文本转换

第一个实际脚本将 UNIX 风格的文本转换成 DOS/Windows 格式。您可能知道,基于 DOS/Windows 的文本文件在每一行末尾有一个 CR(回车)和 LF(换行),而 UNIX 文本只有一个换行。有时可能需要将某些 UNIX 文本移至 Windows 系统,该脚本将为您执行必需的格式转换。

 $ sed -e 's/$/\r/' myunix.txt > mydos.txt

在该脚本中,'$' 规则表达式将与行的末尾匹配,而 '\r' 告诉 sed 在其之前插入一个回车。在换行之前插入回车,立即,每一行就以 CR/LF 结束。请注意,仅当使用 GNU sed 3.02.80 或以后的版本时,才会用 CR 替换 '\r'。

最著名的是 bash,只要一遇到回车,它就会出问题。以下 sed 调用将把 DOS/Windows 格式的文本转换成可信赖的 UNIX 格式:

 $ sed -e 's/.$//' mydos.txt > myunix.txt

该脚本的工作原理很简单:替代规则表达式与一行的最末字符匹配,而该字符恰好就是回车。我们用空字符替换它,从而将其从输出中彻底删除。如果使用该脚本并注意到已经删除了输出中每行的最末字符,那么,您就指定了已经是 UNIX 格式的文本文件。也就没必要那样做了!

6、反转行功能(在简明sed的最后已经有这个了,这里再次提示)

$ sed -e '1!G;h;$!d' forward.txt > backward.txt


放上一个哥们的终极实例:

sed QIF 魔法

过去几个星期,我一直想买一份 Quicken来结算我的银行帐户。Quicken 是一个非常好的金融程序,当然会成功地完成这项工作。但是,经过考虑之后,我觉得自己可以轻易编写某个软件来结算我的支票簿。我想,毕竟,我是个软件开发人员!

我开发了一个很好的小型支票簿结算程序(使用 awk),它通过分析包含我的所有交易的文本文件的语法来计算余额。略微调整之后,我将其改进,以便可以象 Quicken 那样跟踪不同的贷款和借款类别。但是,我还要添加一个特性。最近,我将帐户转移到一家有联机 Web 帐户界面的银行。有一天,我注意到,这家银行的 Web 站点允许以 Quicken 的 .QIF 格式下载我的帐户信息。我马上觉得,如果可以将该信息转换成文本格式,那就太棒了。


两种格式的故事

在查看 QIF 格式之前,先看一下我的 checkbook.txt 格式:

 28 Aug 2000     food    -       -       Y     Supermarket             
 30.94 25 Aug 2000     watr    -       103     Y     Check 103          52.86

在我的文件中,所有字段都由一个或多个制表符分开,每个交易占据一行。日期之后的下一个字段列出支出类型(如果是收入项,则为 "-")。第三个字段列出收入类型(如果是支出项,则为 "-")。然后,是一个支票号字段(如果为空,则还是 "-"),一个交易完成字段("Y" 或 "N"),一个注释和一个美元金额字段。现在,让我们看一下 QIF 格式。当用文本查看器查看下载的 QIF 文件时,它看起来如下:

 !Type:Bank D08/28/2000 T-8.15 N PCHECKCARD SUPERMARKET ^ 
 D08/28/2000 T-8.25 N PCHECKCARD PUNJAB RESTAURANT ^ 
 D08/28/2000 T-17.17 N PCHECKCARD SUPERMARKET

浏览过文件之后,不难猜出其格式 -- 忽略第一行,其余的格式如下:

 D<数据>  
T<交易量>  
N<支票号>  
P<描述>  
^   (这是字段分隔符)

开始处理

在处理象这样重要的 sed 项目时,不要气馁 -- sed 允许您将数据逐渐修改成最终形式。在进行当中,可以继续细化 sed 脚本,直到输出与预期的完全一样为止。无需在试第一次时就保证其完全正确。

要开始,首先创建一个名为 "qiftrans.sed" 的文件,然后开始修改数据:

 1d /^^/d s/[[:cntrl:]]//g

第一个 '1d' 命令删除第一行,第二个命令从输出除去那些讨厌的 '^' 字符。最后一行除去文件中可能存在的任何控制字符。既然在处理外来文件格式,我想消除在中途遇到任何控制字符的风险。到目前为止,一切顺利。现在,要向该基本脚本中添加一些处理功能:

 1d /^^/d s/[[:cntrl:]]//g /^D/ { 
 s/^D\(.*\)/\1\tOUTY\tINNY\t/ 
 s/^01/Jan/         s/^02/Feb/
 s/^03/Mar/         s/^04/Apr/
 s/^05/May/         s/^06/Jun/ 
 s/^07/Jul/         s/^08/Aug/ 
 s/^09/Sep/         s/^10/Oct/
 s/^11/Nov/         s/^12/Dec/ 
 s:^\(.*\)/\(.*\)/\(.*\):\2 \1 \3:  }

首先,添加一个 '/^D/' 地址,以便 sed 只在遇到 QIF 数据字段的第一个字符 'D' 时才开始处理。当 sed 将这样一行读入其模式空间时,将按顺序执行花括号中的所有命令。

花括号中的第一个命令将把如下行:

 D08/28/2000

变换成:

 08/28/2000	OUTY	INNY

当然,现在的格式还不完美,但没关系。我们将在进行过程中逐渐细化模式空间的内容。后面 12 行的最后效果是将数据变换成三个字母的格式,最后一行从数据中除去三个斜杠。最后得到这一行:

 Aug 28 2000	OUTY	INNY

OUTY 和 INNY 字段是占位符,以后将被替换。现在还不能确定它们,因为如果美元金额为负,将把 OUTY 和 INNY 设置成 "misc" 和 "-",但是,如果美元金额为正,将分别把它们更改成 "-" 和 "inco"。既然还没有读入美元金额,所以,需要暂时使用占位符。


细化

现在进一步细化:

 1d  /^^/d s/[[:cntrl:]]//g  /^D/ {
 s/^D\(.*\)/\1\tOUTY\tINNY\t/ 
 s/^01/Jan/          s/^02/Feb/
         s/^03/Mar/          s/^04/Apr/
		 s/^05/May/          s/^06/Jun/
		 s/^07/Jul/          s/^08/Aug/
		 s/^09/Sep/          s/^10/Oct/
		 s/^11/Nov/          s/^12/Dec/
		 s:^\(.*\)/\(.*\)/\(.*\):\2 \1 \3:
		 N          N          N          
		 s/\nT\(.*\)\nN\(.*\)\nP\(.*\)/NUM\2NUM\t\tY\t\t\3\tAMT\1AMT/
		 s/NUMNUM/-/          s/NUM\([0-9]*\)NUM/\1/
		 s/\([0-9]\),/\1/  }

后七行有些复杂,所以将详细讨论它们。首先,连续使用三个 'N' 命令。'N' 命令告诉 sed 将 下一行读入输入中,然后将其附加到当前模式空间。这三个 'N' 命令导致将下三行附加到当前模式空间缓冲区,现在这一行看起来如下:

 28 Aug 2000	OUTY	INNY	\nT-8.15\nN\nPCHECKCARD SUPERMARKET

sed 的模式空间变得很难看 -- 需要除去额外的新行,并执行某些附加的格式化。要这样做,将使用替代命令。要匹配的模式为:

 '\nT.*\nN.*\nP.*'

这将与后面依次跟有 'T'、零或多个字符、新行、'N'、任何数量的字符、新行、'P'、以及任何数量字符的新行匹配。呀!这个规则表达式将与刚刚附加到模式空间的三行的全部内容匹配。但我们要重新格式化该区域,而不是整个替换它。美元金额、支票号(如果有的话)和描述需要出现在替换字符串中。要这样做,我们用带有反斜杠的圆括号括起那些“感兴趣部分”,以便可以在替换字符串中引用它们(使用 '\1'、'\2\ 和 '\3' 来告诉 sed 将它们插入到何处)。以下是最后的命令:

 s/\nT\(.*\)\nN\(.*\)\nP\(.*\)/NUM\2NUM\t\tY\t\t\3\tAMT\1AMT/

该命令将我们的行变换成:

  28 Aug 2000  OUTY  INNY  NUMNUM    Y	   CHECKCARD SUPERMARKET	 AMT-8.15AMT

虽然该行正变得好一些,但是,有几件事一看就有点...啊...有趣。首先是那个愚蠢的 "NUMNUM" 字符串 -- 其目的何在?如果查看 sed 脚本的后两行,就会发现其目的,后两行将把 "NUMNUM" 替换成 "-",而把 "NUM"<number>"NUM" 替换成 <number>。如您所见,用愚蠢的标记括起支票号允许我们在该字段为空时方便地插入一个 "-"。


结束尝试

最后一行除去数字后的逗号。它把如 "3,231.00" 这样的美元金额转换成我使用的格式 "3231.00"。现在,让我们看一下最终脚本:

最终的“QIF 到文本”脚本

 1d /^^/d s/[[:cntrl:]]//g /^D/ { 	s/^D\(.*\)/\1\tOUTY\tINNY\t/ 
 s/^01/Jan/ 	s/^02/Feb/ 	s/^03/Mar/ 	s/^04/Apr/ 	s/^05/May/ 
 s/^06/Jun/ 	s/^07/Jul/ 	s/^08/Aug/ 	s/^09/Sep/ 	s/^10/Oct/ 
 s/^11/Nov/ 	s/^12/Dec/ 	s:^\(.*\)/\(.*\)/\(.*\):\2 \1 \3: 
 N 	N 	N 	s/\nT\(.*\)\nN\(.*\)\nP\(.*\)/NUM\2NUM\t\tY\t\t\3\tAMT\1AMT/ 
 s/NUMNUM/-/ 	s/NUM\([0-9]*\)NUM/\1/ 	s/\([0-9]\),/\1/ 
 /AMT-[0-9]*.[0-9]*AMT/b fixnegs 
 s/AMT\(.*\)AMT/\1/ 	s/OUTY/-/ 	s/INNY/inco/ 
 b done :fixnegs 	s/AMT-\(.*\)AMT/\1/ 	s/OUTY/misc/ 
 s/INNY/-/ :done }

附加的十一行使用替代和一些分支功能来美化输出。首先看一下这行:

         /AMT-[0-9]*.[0-9]*AMT/b fixnegs

该行包含一个格式为 "/regexp/b label" 的分支命令。如果模式空间与规则表达式匹配,sed 将分支到 fixnegs 标号。您应该可以轻易找到该标号,它在代码中为 ":fixnegs"。如果规则表达式不匹配,则以常规方式继续处理下一个命令。

既然您理解该命令本身的工作原理,让我们看一下分支。如果看一下分支规则表达式,将看到它与后面依次跟有 '-'、任意数量的数字、一个 '.'、任意数量的数字和 'AMT' 的字符串 'AMT' 匹配。就象我确信您已猜到一样,该规则表达式专门处理负的美元金额。在这之前,用 'ATM' 括起美元金额,以便以后可以轻易找到它。因为规则表达式只与以 '-' 开始的美元金额匹配,所以,该分支只在恰巧处理借款时才发生。如果正处理贷款,应该将 OUTY 设置成 'misc',将 INNY 设置成 '-',并且应该除去贷款数量前面的负号。如果跟踪代码的流程,将看到实际情况正是这样。如果不执行分支,则用 '-' 替换 OUTY,用 'inco' 替换 INNY。完成了!现在输出行是完美的:

 28 Aug 2000	misc	-	-       Y     CHECKCARD SUPERMARKET  -8.15


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值