通用线程--sed 实例,第3部分( 三 )


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(.*)/NUM2NUMttYtt3tAMT1AMT/
s/NUMNUM/-/ s/NUM([0-9]*)NUM/1/
s/([0-9]),/1/ }

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

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

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

该命令将我们的行变换成:28 Aug 2000 OUTY INNY NUMNUM Y CHECKCARD SUPERMARKET AMT-8.15AMT

虽然该行正变得好一些,但是,有几件事一看就有点...啊...有趣 。首先是那个愚蠢的 "NUMNUM" 字符串 -- 其目的何在?如果查看 sed 脚本的后两行,就会发现其目的,后两行将把 "NUMNUM" 替换成 "-",而把 "NUM""NUM" 替换成 。如您所见,用愚蠢的标记括起支票号允许我们在该字段为空时方便地插入一个 "-" 。结束尝试
最后一行除去数字后的逗号 。它把如 "3,231.00" 这样的美元金额转换成我使用的格式 "3231.00" 。现在,让我们看一下最终脚本:最终的“QIF 到文本”脚本 1d /^^/d s/[[:cntrl:]]//g /^D/ { s/^D(.*)/1tOUTYtINNYt/
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(.*)/NUM2NUMttYtt3tAMT1AMT/
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 2000misc-- Y CHECKCARD SUPERMARKET -8.15

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

推荐阅读