ChinaUnix.net 首页 | 博客 | Linux | 论坛 | 人才 | 培训 | 知识库 | 资料 | 读书 | 手册 | 精华 | 下载 | 沙龙 | 搜索
Linux首页 | Linux论坛 | 论坛精华 | 开源新闻 | 技术文章 | 专题专栏 | 新手指南 | 迁移方案 | 产品方案 | 开源项目 | 开源图书 | 软件下载 | 人才招聘 | Linux博客
  搜索

  产品与方案
·中科红旗全面打造现代化邮政体系
·红旗助力“网上审批服务” 推动电子政务
·红旗正版化开创呼和浩特网吧建设新起点
·红旗Linux助信息产业部邮件服务器“快跑”
·中标普华Linux 为电子政务信息化保驾护航
·中标普华Linux助力基金产业
·中标普华Office率先支持UOF标准
·中标普华邮件系统助力西藏政府信息化建设
·红旗Linux助力国库集中支付系统改革
·红旗助中信卫星 掀起GIS通信应用风暴
·红旗软件助力烟草总局 全面建设“数字烟草”
·红旗助力“信访阳光工程”打造畅通信访渠道
·红帽联合FIS发布下一代实时核心银行平台
·红旗助力金盾 打造全无忧出入境信息系统
·红旗Linux全力打造中国邮政总局名址信息库
·爱尔兰证交所从Unix迁移到红帽企业Linux
·一流的意大利银行选择使用红帽企业Linux
·PLUS Finanzservice选择使用红帽企业Linux
·红帽助力TransACT Communications 公司
·法国零售业巨头Lapeyre采用Redhat Linux
·旅游预订网站选择使用红帽企业Linux
·马哈拉施特拉邦政府的红帽解决之道
·美国联邦政府案例
·红帽为慕尼黑展览会提供现代化集群系统
·Yuba郡用开源软件和红帽产品提高了效率
·红帽企业Linux助印度理工建立高性能计算中心
·采用红帽Linux 将系统维护时间缩短了65%
·从UNIX迁移到Linux使Peñoles公司获益非浅
·Hikal公司用红帽企业Linux开展任务关键的ERP项目
·KDE3.5.4新版本发布
·芝加哥商业交易所从Unix向Linux迁移
·南方基金管理有限公司成功案例 Red Hat Linux
·广东北电通讯设备有限公司成功案例
·挪威国家石油公司从UNIX迁移到红帽Linux,成本减半
·中央电视台CCTV动画部案例 Red Hat Linux

  图书

鸟哥的Linux私房菜基础学..


Linux程序设计.第3版


Linux设备驱动开发详解


  下载
·Endian Firewall
·linux kernel(Linux 内核)
·CentOS
·Fedora Core 6
·Scientific Linux
·Slackware 11.0
·Gentoo Linux
·ubuntu-6.10-i386服务器版本
·ubuntu-6.10-amd64服务器版
·ubuntu-6.10-i386桌面版
·ubuntu-6.10-amd64桌面版
·Engarde Linux
您的位置: Linux时代 > 技术文档 > 新手入门 >

使用 UNIX 进行文本处理

日期:2006-09-05 作者:Chris Herborth 来自:IBM DW中国


UNIX® 起源于简单的文本处理,并且在它的命令行环境中保留了功能最强大的文本处理工具之一。通过将一系列简单的命令组合在一起,可以完成复杂的文本转换,UNIX 提供的工具允许您构建几乎任何所需的文本处理引擎。

引言

在 UNIX® 诞生之初,人们不大熟悉这种新的操作系统,但他们很快找到了适当的切入点,大学中的研究人员需要一种像样的文本处理环境。因为在那个时候,计算机的处理速度和内存容量有限,所以程序必须很小,并且相对比较简单。这样就产生了 UNIX 中著名的设计思想:“一组工具协同工作,以便完成一项任务”。通过 UNIX 管道将几种很小的、但功能强大的文本处理工具组合在一起,可以对文本进行各种方式的转换和操作。

在本文中,您将简要了解从文件和程序中获得文本、使用 tr 命令对其进行简单的转换、使用 sed 命令进行复杂的搜索和替换操作。然后,您将使用 Perl 编程和脚本语言再次完成这些操作,这样一来您就可以认识到,Perl 的功能非常强大,它可以替代 trsed 命令。

开始之前

如果您希望按照本文中的示例进行实验,请确保您可以使用 UNIX 命令行环境。这可能是本地计算机中的终端模拟程序(在现代桌面中通常称为 终端,如果您习惯使用 Windows®,那么可以使用 Cygwin)、或通过 SSH 访问的远程系统。

本文的示例所使用的 Shell 语法适用于 GNU Bash,有关需要使用的特定语法,请参考您的 Shell 手册(或者可以考虑使用 Bash)。

对文本进行各种操作

在开始使用 UNIX 的各种文本实用程序操作文本之前,需要了解如何获得文本。并且在进行这项工作之前,需要了解 UNIX 的标准输入/输出 (I/O) 流。

标准 C 库(因而,每个 UNIX 程序)定义了三种标准流:输入、输出和错误。有时将它们称为 stdin、stdout 和 stderr,这是在所有 C 程序中用来表示它们的全局变量。

当您在 Shell 中使用 > 操作符将程序输出重定向到文件时,就可以将它的标准输出 (stdout) 流发送到这个文件。例如:ls > this-dirls 的输出发送到一个名为 this-dir 的文件。

当您在 Shell 中使用 < 操作符将程序输入重定向到文件时,就可以将该文件中的内容输入到该程序的标准输入 (stdin) 流。例如:sort < this-dir 可以从名为 this-dir 的文件中读取内容,并将其作为 sort 命令的输入。

另一个常用于重定向标准流的操作符是“|”(管道)操作符,它可以将左侧程序的标准输出流连接到右侧程序的标准输入流。例如:ls | sort 和前面的两个示例完成相同的任务,并且无需临时文件,ls 的输出直接进入 sort 命令。

如果您仔细观察,那么可能会发现,前面的这些示例中并没有出现标准错误 (stderr) 流。与标准输出流一样,可以对 stderr 进行重定向或使用管道进行传输,但是您需要告诉 Shell 您希望处理 stderr 而不是 stdout。

可以使用 2> 操作符将标准错误流重定向到文件。在处理生成有用的错误输出的命令时,您经常会看到这个操作符,比如用于编译 UNIX 程序的 make 工具:make 2> build-errors

这个命令运行了 make,并将任何错误信息发送到 build-errors 文件。与之类似,您可以使用 2| 将 stderr 通过管道传递到另一个程序。

如果您对具体的细节感兴趣,那么其他的流也有与之对应的数字,尽管很少使用到它们(0 表示标准输入,1 表示标准输出),除了在一个非常常见的操作符中。在清单 1 所示的示例中,2>&1 操作符将标准错误流连接 到标准输出流。与 > 操作符组合在一起,您可以使 stderr 和 stdout 输出到相同的文件中。


清单 1. 将标准错误流连接到标准输出流
make > build-output 2>&1

命令

有两个常用来生成文本输出的标准 UNIX 命令:catecho

cat 命令读取参数中指定的每个文件,并将这些文件的内容写入到 stdout。echo 命令将其参数写入到 stdout。您常常会发现它们作为更复杂的命令管道中的一部分(请参见清单 2)。


清单 2. 使用 cat 和 echo
cat file1 file2 ... filen
echo arguments...

但如果您只需要文件中开头的部分或结尾的部分,那又应该如何呢?cat 有两种可用来完成这种任务的变种,称为 head 和 tail(请参见清单 3),它们分别可以显示开头的或结尾的 10 行内容,您可以使用 -n 选项为它们指定不同的行数。
清单 3. 使用 head 和 tail
head file1 file2 ... filen
tail file1 file2 ... filen

tail 命令还有一个有用的选项 -f (follow)。该选项告诉 tail 打印指定文件的最后 10 行,但是它不仅打印已有的内容,还会等待该文件中将要出现的更多内容,并对其进行打印。您可以使用该选项接着 显示错误日志中的输出,例如,要在将错误写入到日志的同时查看这些错误。

转换文本

既然您已经了解了至少 5 种生成文本的方式,下面让我们来看一些进行简单文本转换的示例。

tr 命令允许您将一个集合中的字符转换为另一个集合中相应的字符。让我们来看一些示例(清单 4),以了解其工作方式。


清单 4. 使用 tr 对字符进行转换
echo "a test" | tr t p
echo "a test" | tr aest 1234
echo "a test" | tr -d t
echo "a test" | tr '[:lower:]' '[:upper:]'

研究这些命令的输出结果(请参见清单 5),可以看出 tr 的工作方式(提示:它直接使用第二个集合中相应的字符来代替第一个集合中的字符)。


清单 5. tr 进行了哪些工作?
chrish@dhcp3 [199]$ echo "a test" | tr t p
a pesp

chrish@dhcp3 [200]$ echo "a test" | tr aest 1234
1 4234

chrish@dhcp3 [201]$ echo "a test" | tr -d t
a es

chrish@dhcp3 [202]$ echo "a test" | tr '[:lower:]' '[:upper:]'
A TEST

第一个和第二个示例都很简单,将一个字符替换为另一个字符。第三个示例使用了 -d 选项 (delete),它从输出中彻底删除了指定的字符。这个选项通常用来从 DOS 文本文件中删除回车,以将其转换为 UNIX 文本文件(请参见清单 6)。最后一个示例使用了字符类([: :] 中的名称),以将所有的小写字母转换为大写字母。可移植操作系统接口标准(POSIX 标准)字符类包括:

  • alnum:字母数字字符
  • alpha:字母字符
  • cntrl:控制(非打印)字符
  • digit:数字字符
  • graph:图形字符
  • lower:小写字母字符
  • print:可打印字符
  • punct:标点符号
  • space:空白字符
  • upper:大写字符
  • xdigit:十六进制字符

清单 6. 将 DOS 文本文件转换为 UNIX 文本文件
tr -d '\r' < input_dos_file.txt > output_unix_file.txt

尽管 tr 命令表示了 C locale 环境变量(有关这些环境变量更多的信息,可以使用 man locale),但是不要指望它能够对 UTF-8 文档进行任何合理的操作,如能够使用合适的大写字符替换小写重音字符。tr 命令最适合于 ASCII 和其他标准 C 区域设置。

使用 sed 进行复杂的搜索和替换

tr 命令所提供的单字符替换(或删除)功能非常适用于特定的解决方案,但是这些功能并不是很灵活。如果您需要将一个单词替换为另一个单词,或将连续的空格和制表符替换为一个空格,那又应该怎么办呢?

幸运的是,您可以使用 sed 命令 (Stream EDitor),它提供了功能强大的正则表达式 匹配和替换。正则表达式是使用各种构件构建的复杂模式规范,并且随着模式变得越来越复杂,它看起来就像是调制解调器的线路噪声。本文并不打算详细地介绍正则表达式,但是在本文中,您将简单了解 sed 所使用的一些有用的模式。

在清单 7 中,您可以看到 sed 命令的基本格式。模式是用来匹配输入(通常可以使用管道从另一个程序输入,或者重定向于文本文件)的正则表达式,替换是指插入某些文本并用其代替那些与模式相匹配的文本。标志是用来控制替换行为的单个字符。最常用的标志是 g(将替换应用于所有匹配模式的非重叠实例,而不仅仅是第一个匹配项)。

实际上,模式和替换可以是各种各样的内容,并且它们之间不需要像在 tr 命令中那样具有 1:1 的关系。


清单 7. sed 命令
 
sed -e s/pattern/replacement/flags

最简单的模式是一个或多个字符组成的字符串。如清单 8 所示,例如将单词 one 替换为单词 another。


清单 8. 最简单的正则表达式
 
chrish@dhcp3 [334]$ echo "Replace one word" | sed -e s/one/another/
Replace another word

可以使用方括号将一个或多个字符括起来,以创建一个集合,该集合中的任何字符都可以匹配。如清单 9 所示,让我们将所有的元音字母替换为下划线。


清单 9. 匹配集合中的任何字符
 
chrish@dhcp3 [338]$ echo "This is a test" | sed -e s/[aeiouy]/_/g
Th_s _s _ t_st

请注意,示例中使用了 g 标志,以便将模式/替换应用于所有的匹配项,而不仅仅是第一个匹配项。

sed 命令也可以理解 tr 命令所支持的那些命名字符类,POSIX 对这些字符类进行了定义,但是本文中的语法稍有不同。清单 10 显示了如何替换任何空白字符(制表符、空格等等):


清单 10. 根据命名字符类匹配内容
 
chrish@dhcp3 [345]$ echo -e 'hello\tthere'   
hello   there
chrish@dhcp3 [346]$ echo -e 'hello\tthere' | sed -e 's/[[:space:]]/, /'
hello, there

echo 命令的 -e 标志用来告诉该命令扩展 C 风格的转义字符,在本示例中,它会把 \t 转换为制表符。

您还可以使用“.”(点号)匹配任何单个的字符。如果您需要处理一些略有变化的数据,或者包含难以进行转义的特殊字符的数据,那么使用这个符号是非常方便的。例如,在匹配引号时,我经常使用 .,所以我不需要在 Shell 中对引号进行转义。清单 11 显示了一个正则表达式初学者在使用这个模式时出现的问题。


清单 11. 这可能并不是想要的结果
 
chrish@dhcp3 [339]$ echo "This is a test" | sed -e s/./_/g
______________

既然您已经了解了这些非常基本的内容,下面介绍一些附加模式修饰符,要使用高级 正则表达式,您现在还可以使用 -E 选项代替 -e? 字符表示匹配前面模式元素的零个或一个实例,* 字符表示匹配前面元素的零个或多个实例。+ 字符表示匹配一个或多个前面的元素。^ 字符匹配行首,而 $ 则匹配行尾。清单 12 显示了实际应用中的情况。


清单 12. 实际应用中的多个匹配项
 
chrish@dhcp3 [356]$ echo "hellooooo" | sed -E 's/o?$/_/g'
helloooo_
chrish@dhcp3 [357]$ echo "hellooooo" | sed -E 's/o*$/_/g'
hell_
chrish@dhcp3 [358]$ echo "hellooooo" | sed -E 's/o+$/_/g'
hell_

如果使用圆括号将模式元素括起来,您可以在替换字符串中使用匹配的内容。这些元素称为组,它们使得正则表达式搜索和替换操作的功能变得非常强大,但是却很难理解。例如,在清单 13 中,您匹配一个或多个 l (el) 字符,并且后面跟着零个或多个 o 字符。依次使用第二组和第一组中的内容对其进行替换,实际上是对它们进行交换。请注意这个模式中各个组的引用方法,即反斜杠加上该组的序号。


清单 13. 匹配组
 
chrish@dhcp3 [361]$ echo "hellooooo" | sed -E 's/(l+)(o*)$/\2\1/g'
heoooooll

通过在大括号中指定匹配的数目,您可以匹配特定数目的模式。例如,模式 o{2} 将匹配两个(仅仅两个)o 字符。

对了,还有最后一个内容,通过使用 \ 字符对其进行转义,您可以在模式中使用这些特殊字符的字面内容(即作为其本身)。

将其组合在一起

既然已经向您介绍了一些非常简单的正则表达式,那么让我们来尝试一些有用的内容。给定 ls -l(文件 清单)的输出,您将从中提取权限信息、大小和名称。清单 14 显示了要进行处理的 ls -l 输出示例。


清单 14. ls -l 的典型输出
 
chrish@dhcp3 [365]$ ls -l | tail
drwx------   3 chrish    wheel   102 Jun 14 21:38 gsrvdir501
drwxr-xr-x   2 chrish    wheel    68 Jun 16 16:01 hsperfdata_chrish
drwxr-xr-x   3 root      wheel   102 Jun 14 23:38 hsperfdata_root
-rw-r--r--   1 root      wheel   531 Jun 14 10:17
 illustrator_activation.plist
-rw-r--r--   1 root      wheel   531 Jun 14 10:10 indesign_activation.plist
-rw-------   1 nobody    wheel    24 Jun 16 16:01 objc_sharing_ppc_4294967294
-rw-------   1 chrish    wheel   132 Jun 16 23:50 objc_sharing_ppc_501
-rw-------   1 security  wheel    24 Jun 16 10:04 objc_sharing_ppc_92
-rw-r--r--   1 root      wheel   531 Jun 14 10:05 photoshop_activation.plist
-rw-r--r--   1 root      wheel   928 Jun 14 10:17 serialinfo.plist

正如您所看到的,这里一共有 7 列:

  • 权限
  • 链接的数目
  • 属主
  • 大小
  • 最后的修改时间
  • 名称
让我们来建立一些正则表达式,以匹配其中的每一列:
  • .([r-][w-][x-]){3}—权限(使用 . 匹配第一个字符,因为它可能是几个不同的特殊字符中的任何一个。)
  • [[:digit:]]+—链接的数目
  • [A-Za-z0-9_\-\.]+ -—属主(您还可以使用这个模式进行组匹配。)
  • [[:digit:]]+—大小
  • .{3} [0-9 ]{2} [0-9 ][0-9]:[0-9][0-9]—修改时间(您可以对这个模式进行一些简化,因为所有的文件都在 6 月份进行的修改,所以您可以确切地指定月份的名称。)
  • .+$—名称(在这些内容之后,您需要匹配所有的字符,直到行尾。)

在上述模式之间,必须使用 [[:space:]]+ 对它们进行连接,因为您并不知道这些列之间究竟是使用空格或制表符,还是两者的组合进行分隔。您还需要将权限、大小和名称放到组中,以便可以在替换中使用它们。如清单 15 所示,正则表达式很快就变得难以理解。


清单 15. 完成后的正则表达式实在难以理解!
 
(.([r-][w-][x-]){3})[[:space:]]+[[:digit:]]+[[:space:]]+([A-Za-z0-9_\-\.]
+[[:space:]]+){2}([[:digit:]]+)[[:space:]]+.{3} [0-9 ]{2} [0-9
 ][0-9]:[0-9][0-9][[:space:]]+(.+)$

如果您仔细研究这个可怕的正则表达式模式,您将发现 5 个组:

  1. 完整的权限块
  2. 权限块中最后匹配的 rwx 组
  3. 组(该模式的属主/组部分中最后匹配的内容)
  4. 大小
  5. 名称

在清单 16 中,您将更改 ls -l 的输出以显示文件名、权限和大小。


清单 16. 对输出进行重组
 
chrish@dhcp3 [382]$ ls -l | tail | sed -E
 's/(.([r-][w-][x-]){3})[[:space:]]+[[:digit:]]+[[:space:]]+([A-Za-z0-9_\-\.
 ]+[[:space:]]+){2}([[:digit:]]+)[[:space:]]+.{3} [0-9 ]{2} [0-9
 ][0-9]:[0-9][0-9][[:space:]]+(.+)$/\5 (\1) has \4 bytes of data/'
gsrvdir501 (drwx------) has 102 bytes of data
hsperfdata_chrish (drwxr-xr-x) has 68 bytes of data
hsperfdata_root (drwxr-xr-x) has 102 bytes of data
illustrator_activation.plist (-rw-r--r--) has 531 bytes of data
indesign_activation.plist (-rw-r--r--) has 531 bytes of data
objc_sharing_ppc_4294967294 (-rw-------) has 24 bytes of data
objc_sharing_ppc_501 (-rw-------) has 132 bytes of data
objc_sharing_ppc_92 (-rw-------) has 24 bytes of data
photoshop_activation.plist (-rw-r--r--) has 531 bytes of data
serialinfo.plist (-rw-r--r--) has 928 bytes of data

成功了!您已经完成了对输出结果的转换。

使用 Perl 完成相应的工作

Perl 编程和脚本语言(请参见参考资料部分)的功能非常强大,通常可用来取代前面介绍的 trsed 命令。通常可以在命令行中直接输入简短的 Perl 程序,有时它可以完成比 trsed 命令行更多的操作。

Perl 的 -p 选项告诉它读取和处理标准输入中的每行内容,并将结果打印到标准输出。-e 选项允许您在命令行中指定一个 Perl 表达式(实际上是一个程序)。

清单 17 显示了如何使用 Perl 完成清单 5 中的示例。


清单 17. 使用 Perl 完成 tr 的工作
 
chrish@dhcp3 [248]$ echo a test | perl -p -e 'tr/t/p/;'
a pesp

chrish@dhcp3 [249]$ echo a test | perl -p -e 'tr/aest/1234/;'
1 4234

chrish@dhcp3 [250]$ echo a test | perl -p -e 'tr/t//d;'
a es

chrish@dhcp3 [251]$ echo a test | perl -p -e 'tr/a-z/A-Z/;'
A TEST

Perl 的 tr 语句具有不同的语法,它更像 sed 的搜索和替换表达式。另请注意,您在最后一个示例中指定了小写和大写字符的范围。

Perl 中的正则表达式支持非常优秀,并且上面的 sed 示例可以作为有效的 Perl 语句正常工作。清单 18 使用 Perl 显示了清单 16 中的 ls -l 示例,除了 Perl 命令行语法之外,不需要对其他的内容进行更改。


清单 18. 使用 Perl 重组 ls 的输出
 
chrish@dhcp3 [384]$ ls -l | tail | perl -p -e
 's/(.([r-][w-][x-]){3})[[:space:]]+[[:digit:]]+[[:space:]]+([A-Za-z0-9_\-\.]
+[[:space:]]+){2}([[:digit:]]+)[[:space:]]+.{3} [0-9 ]{2} [0-9
 ][0-9]:[0-9][0-9][[:space:]]+(.+)$/\5 (\1) has \4 bytes of data/'
gsrvdir501 (drwx------) has 102 bytes of data
hsperfdata_chrish (drwxr-xr-x) has 68 bytes of data
hsperfdata_root (drwxr-xr-x) has 102 bytes of data
illustrator_activation.plist (-rw-r--r--) has 531 bytes of data
indesign_activation.plist (-rw-r--r--) has 531 bytes of data
objc_sharing_ppc_4294967294 (-rw-------) has 24 bytes of data
objc_sharing_ppc_501 (-rw-------) has 132 bytes of data
objc_sharing_ppc_92 (-rw-------) has 24 bytes of data
photoshop_activation.plist (-rw-r--r--) has 531 bytes of data
serialinfo.plist (-rw-r--r--) has 928 bytes of data

这样做的优点在于,您可以使用 sed 或 Perl 完善正则表达式,并且在只包含其中某一个的系统中,您仍然可以它们。使用 Perl,您可以获得全方位的编程结构,可以充分地利用它们进行更复杂的文本处理。

总结

使用像 sed 和 Perl 这样功能强大的工具,以及神奇的正则表达式,您可以直接通过 UNIX 命令行轻松地完成复杂的文本处理任务。这使得您可以有效地将多个命令组合在一起,以正确地完成文本处理工作。

原文链接:http://www-128.ibm.com/developerworks/cn/aix/library/au-textprocess.html

本文被浏览



 相关新闻



 相关评论
关于我们 | 联系方式 | 广告合作 | 诚聘英才 | 网站地图 | 免费注册

Copyright © 2001-2006 ChinaUnix.net All Rights Reserved

感谢所有关心和支持过ChinaUnix的朋友们

京ICP证041476号