sed教程入门与实例练习(三)

在第二篇 sed 文章中,我提供了一些示例来演示 sed 的工作原理,但是它们当中很少有示例能实际做特别有用的事。在这篇 sed 系列的最后文章中,我要改变那种方式,并使用 sed 来做实际的事。我将为您显示几个示例,它们不仅演示 sed 的能力,而且还做一些真正巧妙(和方便)的事。例如,在本文的后半部,将为您演示如何设计一个 sed 脚本来将 .QIF 文件从 Intuit 的 Quicken 金融程序转换成具有良好格式的文本文件。在那样做之前,我们将看一下不怎么复杂但却很有用的 sed 脚本。

文本转换
第一个实际脚本将 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’。如果还没有安装 GNU sed 3.02.80,请在我的第一篇 sed 文章中查看如何这样做的说明。

我已记不清有多少次在下载一些示例脚本或 C 代码之后,却发现它是 DOS/Windows 格式。虽然很多程序不在乎 DOS/Windows 格式的 CR/LF 文本文件,但是有几个程序却在乎 — 最著名的是 bash,只要一遇到回车,它就会出问题。以下 sed 调用将把 DOS/Windows 格式的文本转换成可信赖的 UNIX 格式:

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

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

反转行
下面是另一个方便的小脚本。与大多数 Linux 发行版中包括的 “tac” 命令一样,该脚本将反转文件中行的次序。”tac” 这个名称可能会给人以误导,因为 “tac” 不反转行中字符的位置(左和右),而是反转文件中行的位置(上和下)。用 “tac” 处理以下文件:

foo bar oni

….将产生以下输出:

oni bar foo

可以用以下 sed 脚本达到相同目的:

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

如果登录到恰巧没有 “tac” 命令的 FreeBSD 系统,将发现该 sed 脚本很有用。虽然方便,但最好还是知道该脚本为什么那样做。让我们对它进行讨论。

反转解释
首先,该脚本包含三个由分号隔开的单独 sed 命令:’1!G’、’h’ 和 ‘$!d’。现在,需要好好理解用于第一个和第三个命令的地址。如果第一个命令是 ‘1G’,则 ‘G’ 命令将只应用第一行。然而,还有一个 ‘!’ 字符 — 该 ‘!’ 字符忽略该地址,即,’G’ 命令将应用到除第一行之外的所有行。’$!d’ 命令与之类似。如果命令是 ‘$d’,则将只把 ‘d’ 命令应用到文件中的最后一行(’$’ 地址是指定最后一行的简单方式)。然而,有了 ‘!’ 之后,’$!d’ 将把 ‘d’ 命令应用到除最后一行之外的所有行。现在,我们所要理解的是这些命令本身做什么。

当对上面的文本文件执行反转脚本时,首先执行的命令是 ‘h’。该命令告诉 sed 将模式空间(保存正在处理的当前行的缓冲区)的内容复制到保留空间(临时缓冲区)。然后,执行 ‘d’ 命令,该命令从模式空间中删除 “foo”,以便在对这一行执行完所有命令之后不打印它。

现在,第二行。在将 “bar” 读入模式空间之后,执行 ‘G’ 命令,该命令将保留空间的内容 (”foo/n”) 附加到模式空间 (”bar/n”),使模式空间的内容为 “bar/n/foo/n”。’h’ 命令将该内容放回保留空间保护起来,然后,’d’ 从模式空间删除该行,以便不打印它。

对于最后的 “oni” 行,除了不删除模式空间的内容(由于 ‘d’ 之前的 ‘$!’)以及将模式空间的内容(三行)打印到标准输出之外,重复同样的步骤。

现在,要用 sed 执行一些强大的数据转换。

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

别犯糊涂
如您所见,只要循序渐进地解决问题,使用 sed 转换数据就没有那么难。不要试图使用一个 sed 命令或一下子解决所有问题。相反,要朝着目标逐步进行,并不断改进 sed 脚本,直到其输出正如您希望那样为止。sed 有许多功能,希望您已非常熟悉其内部工作原理并继续努力以进一步掌握它!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/535769.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Oracle GoldenGate微服务架构

Oracle GoldenGate支持两种架构&#xff0c;经典架构和微服务架构&#xff08;MA&#xff09;。 可以出于以下目的配置Oracle GoldenGate&#xff1a; 从一个数据库中静态提取数据记录&#xff0c;并将这些记录加载到另一个数据库中。连续提取和复制事务性数据处理语言&#…

Oracle GoldenGate经典架构

可以使用Oracle GoldenGate Classic Architecture从命令行配置和管理数据复制。 图示的说明logicalarch2.png 注意&#xff1a; 这是基本配置。根据业务需求和用例&#xff0c;可以配置此模型的不同变体。 1、Manager Manager是Oracle GoldenGate的控制过程。必须先在Oracl…

WordPress 首页显示摘要

这里的方法不需要你另外装插件。 1、使用more标签 (缺点&#xff1a;每次都要加一下这个东西&#xff0c;不灵活只能一刀切。优点&#xff1a;方法比较正规不需要改动模版) 在你需要截断的地方(就是你的编辑框)加 <!–more–> 代码. 2、使用the_excerpt标签 (缺点&#x…

Oracle GoldenGate复制过程

这两种Oracle GoldenGate体系结构共有许多数据复制过程。 1、什么是Extract&#xff1f; Extract是一个过程&#xff0c;该过程被配置为针对源数据库运行或被配置为在下游挖掘数据库&#xff08;仅Oracle&#xff09;上运行&#xff0c;以捕获在其他位置的真实源数据库中生成…

awk教程入门与实例练习(一)

Awk 是一种非常好的语言&#xff0c;同时有一个非常奇怪的名称。在本系列&#xff08;共三篇文章&#xff09;的第一篇文章中&#xff0c;Daniel Robbins 将使您迅速掌握 awk 编程技巧。随着本系列的进展&#xff0c;将讨论更高级的主题&#xff0c;最后将演示一个真正的高级 a…

HDFS-简介

HDFS 是 Hadoop Distribute File System 的简称&#xff0c;意为&#xff1a;Hadoop 分布式文件系统&#xff0c;是一种旨在在商品硬件上运行的分布式文件系统。它与现有的分布式文件系统有许多相似之处。但是&#xff0c;与其他分布式文件系统的区别很明显。HDFS具有高度的容错…

awk教程入门与实例练习(二)

在这篇 awk 简介的续集中&#xff0c;Daniel Robbins 继续探索 awk&#xff08;一种很棒但有怪异名称的语言&#xff09;。Daniel 将演示如何处理多行记录、使用循环结构&#xff0c;以及创建并使用 awk 数组。阅读完本文后&#xff0c;您将精通许多 awk 的功能&#xff0c;而且…

HDFS-配置项

一、core-site.xml与core-default.xml core-default.xml与core-site.xml的功能是一样的&#xff0c;如果在core-site.xml里没有配置的属性&#xff0c;则会自动会获取core-default.xml里的相同属性的值 <configuration><property><!-- 这个属性用来指定namenod…

awk教程入门与实例练习(三)

在 awk 系列的这篇总结中&#xff0c;Daniel 向您介绍 awk 重要的字符串函数&#xff0c;以及演示了如何从头开始编写完整的支票簿结算程序。在这个过程中&#xff0c;您将学习如何编写自己的函数&#xff0c;并使用 awk 的多维数组。学完本文之后&#xff0c;您将掌握更多 awk…

HDFS-常用命令

1. -help&#xff1a;显示帮助信息 hadoop fs -help rmshel2. -ls&#xff1a;显示目录信息 hadoop fs -ls /3. -mkdir&#xff1a;在HDFS上创建目录 hadoop fs -mkdir -p /user/ha4. -moveFromLocal&#xff1a;从本地剪切粘贴到HDFS hadoop fs -moveFromLocal ~/test.txt…

如何关闭WINDOWS2003 DEP数据保护功能

近来很多朋友和客户都使用了WINDOWS2003来架设自己的GAME SERVER,但有很多朋友反映说,不如WINDOWS2000好,原因不是稳定,而是成功率高,和简单.但我个人觉得WINDOWS2003还是不错的系统,如果朋友们都不用这个系统,而用WINDOWS2000 有点不值得了.我就开始找寻这样的问题.我对GAME 不…

JDK源码解析之 java.lang.Thread

位于java.lang包下的Thread类是非常重要的线程类&#xff0c;它实现了Runnable接口&#xff0c;今天我们来学习一下Thread类&#xff0c;在学习Thread类之前&#xff0c;先介绍与线程相关知识&#xff1a;线程的几种状态、上下文切换&#xff0c;然后接着介绍Thread类中的方法的…

TASKLIST

TASKLIST [/S system [/U username [/P [password]]]] [/M [module] | /SVC | /V] [/FI filter] [/FO format] [/NH]参数列表:/S system 指定连接到的远程系统。/U [domain/]user 指定使用哪个用户执行这个命令。/P [password] 为指定的用户指定密码。/SVC 显示每个进程中的服务…

JDK源码解析之 java.lang.ThreadLocal

此类提供线程局部变量。这些变量与普通变量不同&#xff0c;每个访问一个线程&#xff08;通过其get或set方法&#xff09;的线程 都有其自己的&#xff0c;独立初始化的变量副本。 ThreadLocal实例通常是希望将状态与线程关联的类中的私有静态字段&#xff08;例如&#xff0c…

华尔街顶级大师胡立阳名言

1.不要听“亲朋好友”的话&#xff0c;他们只会让你成为“平凡人”。 2.不要只会“用功读书”&#xff0c;重要的是“要读对书”。  3&#xff0e;不要只是“努力工作”&#xff0c;重要的是“做对工作”。   4.不要指示结交“志趣相投”的朋友&#xff0c;否则你永远只看到…

JDK源码解析之 Java.lang.Enum

Enum是一个特殊的类. 我们不能以class Xxx extends Enum的方式手动继承, 必须写成enum Xxx的形式; 然而这段枚举类的定义在编译之后又变回了class Xxx extends Enum. 一、类定义 public abstract class Enum<E extends Enum<E>>implements Comparable<E>, …

Linux下的一些简单网络配置命令介绍

1、 ifconfig可以使用ifconfig命令来配置并查看网络接口的配置情况。例如&#xff1a;&#xff08;1&#xff09; 配置eth0的IP地址&#xff0c; 同时激活该设备。#ifconfig eth0 192.168.1.10 netmask 255.255.255.0 up&#xff08;2&#xff09; 配置eth0别名设备eth0:1的IP地…

JDK源码解析之 java.lang.Throwable

在 Java 中&#xff0c;所有的异常都有一个共同的祖先 Throwable&#xff08;可抛出&#xff09;。Throwable 指定代码中可用异常传播机制通过 Java 应用程序传输的任何问题的共性。 一、类定义 public class Throwable implements Serializable {}Serializable&#xff1a;可…

JDK源码解析之 java.lang.Error

java.lang.Error 错误。是所有错误的基类&#xff0c;用于标识严重的程序运行问题。这些问题通常描述一些不应被应用程序捕获的反常情况。 一、源码部分 //继承了java.lang.Throwable public class Error extends Throwable {//适用于java序列化机制,过判断类的serialVersionU…

linux命令之有关网络的操作命令

1&#xff0e;hostname 命令&#xff08;1&#xff09;一般格式&#xff1a;hostname [选项] [主机名]&#xff08;2&#xff09;说明&#xff1a;显示或设置系统的主机名&#xff1b;如果无任何选项和主机名&#xff0c;则用于显示系统的主机名。&#xff08;3&#xff09…