perl教程011

Perlæ•™&am - 下载 第11学 系统之间的互操作 到现在为止,我们介绍的所有 Perl

Info iconThis preview shows page 1. Sign up to view the full content.

View Full Document Right Arrow Icon
This is the end of the preview. Sign up to access the rest of the document.

Unformatted text preview: 下载 第11学时 系统之间的互操作性 到现在为止,我们介绍的所有 Perl 特性基本上都属于独立的特性。如果你想完成某项操作, 那么你必须自己亲自执行这项操作,比如给数据排序,创建目录列表,插入配置信息等。问 题是它的工作量很大,你必须重复进行可以在其他地方完成的工作。 关于 Perl,现在有一种非常流行的说法,那就是它是一种非常出色的“胶水”语言。它的 意思是说, Perl能够使用操作系统作为组件来安装的其他程序,然后将这些程序组合起来,形 成一个更大的程序。它能够启动操作系统的实用程序,用它们来搜集信息,与你进行通信, 然后将它们关闭。 Perl能够将这些较小的实用程序“胶合”在一起,形成一个大得多而且更加有用的实用程 序。这种能力的好处是使你能够迅速编写在其他情况下需要花费很长时间来编写的代码,并 且能够对代码进行调试。你应该使用对你有用的任何手段,迅速而准确地编写代码。将系统 的实用程序胶合在一起,可使之具备很大的优点。 在本学时中,你将要学习: • system()函数。 • 捕获输出。 • 代码的移植性。 本学时中的大部分代码例子都有两个版本,一个用于 Wi n d o w s 和D O S 系 统,另一个用于 UNIX系统。如果只有一个代码例子,那么你会在课文中找到 关于如何修改该代码,以适合另一种系统的需要,这种修改通常是少量的更 改。 11.1 system()函数 若要运行非 P e r l 的命令,最简单的方法是使用 s y s t e m ( )函数。 s y s t e m ( )函数能够暂停 P e r l 程 序的运行,然后运行外部命令,接着再运行你的 Perl程序。 system函数的句法如下: 该语句中的 c o m m a n d 是你要运行的命令,如果一切正常,系统的返回值是 0,如果出现问 题,则返回非零值。请注意,这个结果与 True和False 这两个 Perl标准返回值相反。 下面是在 UNIX 系统上运行 system函数的一个例子: 下面这个例子显示在 DOS/Windows下运行 system 的情况: 下载 127 第11学时 系统之间的互操作性使用 总的来说, s y s t e m 函数在上述两种系统结构下的运行情况是相同的。应该记住的是,两 种操作系统下运行的命令基本上是不同的。若要在 D O S下获得一个文件列表,可以使用 d i r命 令,而在 U N I X 下获得文件列表,要使用 l s 命令。在非常少见的情况下,比如在 p e r l d o c 中, UNIX和DOS系统下的命令是相同的。 当s y s t e m 函数运行外部命令时,该命令的输出在屏幕上显示的情况与 P e r l程序输出的情况 是相同的。如果该外部命令需要输入数据,那么来自终端的输入与你的 Perl 程序从终端读入的 输入是相同的。 s y s t e m函数运行的命令继承了 S T D I N 和 S T D O U T 文件描述符,因此,外部命 令执行输入/输出的位置与你的 P e r l程序执行输入/输出的位置是完全相同的。通过 s y s t e m函 数来调用完全交互式的程序是可能的。 请看下面这个在 UNIX下运行的代码例子: 现在再看在 Windows/DOS下运行的代码例子: 上面的每个例子都对 m y f i l e . t x t 运行一个编辑器, U N I X 的编辑器是 v i , D O S 的编辑器是 edit。当然编辑器是全屏幕运行的,所有的标准编辑器命令均能运行。当编辑器退出时,控制 权返回给 Perl 。 可以使用 s y s t e m 函数运行任何程序,不只是控制台方式的程序(文本程序) 。在 U N I X下, 下面这个例子运行一个图形时钟: 在DOS/Windows下,下面这个例子用于启动一个图形文件编辑器: 基本的命令解释程序 s y s t e m 函数(以及本学时中介绍的大多数函数)允许你使用命令提示符向你提供的命令 解释程序的特性。之所以能够这样做是因为 P e r l的 s y s t e m 命令能够调用一个 s h e l l(在 U N I X 上是 / b i n / s h,在 D O S/Wi n d o w s 中是 c o m m a n d . e x e ) ,然后将你的 s y s t e m 命令赋予该 s h e l l。这 样,你就能够在 U N I X ( &)下,执行重定向(>) 、管道传输( | )和后台操作等任务,并且 能够使用命令解释程序提供的其他特性。 例如,若要运行一个外部命令,并且捕获它在文件中的输出,可以使用下面这个命令: 上面这个命令用于运行 p e r l d o c 的p e r l f a q 5 ,并且捕获它在文件中的输出,称为 f a q f i l e . t x t。 这个特定语句在 DOS和UNIX中均能运行。 有些特性,比如管道传输和后台操作,在 U N I X操作系统下也能按照你的期望来运行,如 你在下面看到的那样: 128 使用第二部分 高 级 特 性 下载 在最后一个代码举例中, x t e r m这个程序启动运行,但是 &将使 U N I X 的s h e l l 启动“后台” 的进程。这意味着虽然该进程继续运行,但是 s y s t e m 函数已经完成运行,并将控制权返回给 Perl。Perl将不等待 xterm 完成运行。 在 U N I X下, P e r l 总是将/ b i n / s h 或类似的命令用于 s y s t e m函数、管道和反 引号(后面将介绍) 。不管你的个人 s h e l l 被配置成什么,它都是这样使用的。 这种用法提供了在各个 UNIX系统之间的某种程序的可移植性。 本学时中使用 s y s t e m 函数、管道和反引号的例子放到 M a c i n t o s h 系统上可能无法运行。详 细的说明请参见 MacPerl文档中的“ Macintosh的特殊特性”这一节。 11.2 捕获输出 s y s t e m 函数有一个很小的不足,它没有提供特别好的方法,来捕获命令的输出,并将它 送往 Perl进行分析。如果要以迂回方式进行这项操作,你可以使用下面这个代码: 在上面这个代码段中, s y s t e m运行的命令让它的输出转到一个称为 o u t f i l e的文件中。然后 该文件被打开并读入一个数组。这时数据 @data 包含了 dir命令的输入。 这个方法很麻烦,不是一个十分聪明的办法。 Perl有另外一个方法处理这个问题,即反引 号。用反引号( ` `)括起来的任何命令均由 P e r l作为外部命令来运行,就像通过 s y s t e m运行的 一样,其输出被捕获,并且作为反引号的返回值返回。请看下面这个使用反引号的代码例子: 在上面这个代码段中,运行的是 dir命令,其输出在 $directory中捕获。 在反引号中,可以看到所有标准的 s h e l l 处理方式: > 负责重定向, | 负责管道传输。在 U N I X 下, & 负责启动后台任务。不过请记住,在后台运行的命令,或者用 >重定向其输出的 命令,均没有输出可以捕获。 在标量上下文中,反引号返回的命令输出是单个字符串。如果命令的输出包含许多行文 本,那么字符串中出现的所有文本行均用记录分隔符分开。在列表上下文中,命令的输出被 赋予该列表,列表的每一行结尾均有记录分隔符。 现在请看下面这个代码: 在上面这个代码段中, @dir中的输出在 foreach循环中处理,每次处理一行。 Perl还有另一种方法能够起到反引号的作用,你可以使用 qx{}表示法。要执行的命令放入 花括号( {} )中,如下例所示: 下载 129 第11学时 系统之间的互操作性使用 通 过使用花括号,当反引号作为命令的组成部分出现时,可以不必在反引号的前面加上 反斜杠,如下所示: 也可以将上面的代码段改写为下面的形式: 任何字符均可用来取代 {},成对的字符如 < > , ()和 等均可以使用。 避免shell中的概念混乱 Perl与命令解释程序之间的界线有时会变得比较模糊,请看下面的两个例子: 在UNIX系统中: 在DOS和Windows系统中: 在第一个例子中, $ H O M E究竟是 P e r l 变量 $ H O M E ,还是 s h e l l 的环境变量 $ H O M E呢?在 D O S 例子中, % w i n d i r %究竟是 c o m m a n d . c o m 变量 w i n d i r ,还是 P e r l的哈希结构 % w i n d i r 后随符 号%呢? 问题是 $HOME被Perl进行了内插替换,也就是说, $HOME是Perl的标量变量 $HOME ,它 可能不是你想要的。在反引号中,变量展开为它们各自的值,就像使用双引号( )一样。 “” 可能的变量名 %windir并不在双引号中展开,只有标量和数组名进行了内插替换。 为了避免这种混乱,可以在不想让 Perl进行内插替换的变量前面加上一个反斜杠,如下面 这两个例子所示: 或者 现在, $HOME是UNIX shell 的HOME变量, %winddir%是command.com的winddir 变量。 另一种方法是用 qx{}表示法来代替反引号,并用单引号来限定 qx,如下面这些例子所示: 或者 Perl将 qx” 序列视为特殊标号,并且不展开它里面的 Perl变量,因此,你可以使用反引号, 并且不必用反斜杠对命令中出现的其他反引号进行转义。 11.3 管道 U N I X与D O S /Wi n d o w s 中的管道可用于将不同进程连接在一起,使一个进程的输出成为 下一个进程序的输入。请看下面的一组命令,这些命令差不多可以在 UNIX(如果将 dir改为 ls) 或DOS中运行: 130 使用第二部分 高 级 特 性 下载 dir的输出在 outfile 中集中,然后使用 sort对outfile排序,同时 sort的输出被存放在 newfile中。 接着 more 命令显示 newfile的内容,每次显示一屏内容。 管道允许你执行与上面相同的命令序列,但是不带 outfile和newfile,如下所示: d i r的输出被赋予 s o r t,然后 s o r t对数据进行排序。 s o r t 的输出被赋予 m o r e , 每次显示 1页。 它不需要重定向( >)或临时文件。 这种命令行称为管道命令行,两个命令之间的竖线称为管道。 U N I X主要依赖管道来连接 它的较小的实用程序。 D O S 和 Wi n d o w s均支持管道,但与管道一起运行的命令行实用程序要 少得多。 P e r l程序可以用不同方式纳入管道。首先,你可以编写一个 P e r l 程序,以便接收输入,对 输入进行转换,然后插入管道,如下例所示: 在上面这个管道中, To t a l e r可以是你编写的 P e r l 程序,以便输出目录列表的合计,也可能 是某些统计数字,同时输出目录列表本身。如果你使用 U N I X 系统,请将 dir /B 改为 ls -1 ,管 道就会按照你的要求来运行。程序清单 11-1包含 Totaler程序。 程序清单 11-1 Totaler的完整清单 第6行:输入的每一行均从 S T D I N读入,再赋予 $ _ 。在管道上,一个程序的 S T D I N被连接 到前一个程序的 STDOUT。因此,在上面的例子中, STDIN由dir /B馈入信息。 第9 ~ 1 3 行:如果遇到一个目录,在 $ d i r s 中对它的号码单独相加,目录名被输出,循环再 次启动运行。 第14~15行:否则,在 $sizes中对文件的大小进行累加,文件名被输出。 第17~18行:输出文件的平均大小,同时输出文件和目录的总数。 P e r l 参与管道运行的另一种方法是将管道视为既可以读取也可以写入的文件。这是使用 Perl中的 open 函数来实现的,如下所示: 在上面这个代码段中, o p e n函数打开一个管道,以便从 dir/B | sort 中读取数据。 P e r l 从该 131 第11学时 系统之间的互操作性使用 下载 管 道读取数据的这一事实是通过右边的最后一个管道( | )来指明的。当 o p e n 函数运行时, Perl启动执行 dir /B | sort 命令。当文件句柄 RHANDLE被读取时, sort的输出被读入 Perl程序。 现在请看下面这个例子: 这个 open函数打开一个管道,以便将数据写入 more命令。左边的管道符号说明 Perl正在将 数据写入管道。输出到 WHANDLE文件句柄的所有数据均被 more缓存,并每次显示 1页。编写 这样的函数是每次显示你的一页程序的输出的好办法。 当你完成对已向程序打开的文件句柄(如 R H A N D L E 和 W H A N D L E)的操作时,应该正 确地关闭句柄,这一点非常重要,因为由 o p e n 函数打开的程序必须正确地关闭,若要关闭文 件句柄,可以使用 c l o s e函数来确保它的正确关闭。当完成句柄操作后,如果不能关闭文件句 柄,那么即使你的 Perl 程序已经终止运行,你编写的程序仍会继续运行。 当关闭了管道上打开的文件句柄后, c l o s e 函数会指明管道运行是否成功。因此,你应该 像下面这样认真检查 close的返回值: open函数也许无法告诉你管道是否已经成功地启动运行,其原因与 UNIX 的设计有关。当 P e r l 创建管道并将它启动时,它不清楚管道是否真的能够工 作。如果管道组装正确,并且启动运行了,那么它认为它将能够正确地终止 运行。当管道中的最后一个程序完成运行时,它应该返回一个成功退出的状 态。 c l o s e函数能够读取该状态,以了解是否一切运行正常,否则,就会产生 一个错误。 11.4 可移植性入门 可移植性,这是 P e r l擅长的特性之一。无论你的 P e r l代码是在 V M S 计算机上运行,还是在 UNIX 、Macintosh或MS-DOS 系统下运行,都具有很强的可移植性,使你编写的 Perl代码能够 在Perl 支持的任何结构上天衣无缝地运行。当需要与基本的操作系统打交道时,比如当进行文 件输入/输出时, Perl将设法隐藏所有不必要的细节,使你的代码能够实实在在地运行。 P e r l 之所以具有如此强的可移植性,第 1 6 学时将对其中的某些原因进行 详细说明。 不过, Perl能够向你隐藏的信息是要受到一定限制的。 在本学时中,有些代码例子讲明“这适用于 Wi n d o w s和 D O S ,而这适用于 U N I X” ,有时 又说两种系统都适用,具体情况要根据你使用的系统结构而定。使你自己的程序同时适用于 Wi n d o w s 和 U N I X ,这意味着每个程序必须创建两个版本,一个用于 Wi n d o w s ,一个用于 U N I X。当你的程序运行成功并且移植到一个更加特殊的操作系统,如 MacOS 9 ,那么创建程 序的两个版本将会带来更多的问题。 许多情况下,你为一种操作系统结构(如 Windows NT )编写了一个程序,结果却发现它 是在另一种结构(比如 U N I X)中运行。由于 P e r l 能够在那么多的不同结构中运行,因此许多 人认为在 Windows NT 下运行 Perl 程序与它在 UNIX下运行是相同的。 Web服务器和其他应用程 132 使用第二部分 高 级 特 性 下载 序经常在不同的操作系统之间转移,因此使你的软件具备可移植性是个非常好的思路。 为每种操作系统创建一个程序的不同版本,使程序能够在任何情况下都能够运行,这是 很费时间、很浪费和低效率的。遵照一些规则,你就能够创建到处都能运行的程序,至少可 以设法使之到处都能运行,并且便于维护。 下面是编写“到处均可运行的”代码时遵循的一般原则: • 始终使警告特性处于打开状态,并使用 use strict 命令。这样,就可以确保你的代码能够 用不同版本的 Perl来运行,并且不会出现明显的错误。 • 始 终都要检查来自系统请求的返回值,例如,应该使用 open || die ,而决不要只使用 o p e n。检查返回值可以在将应用程序从一个服务器移到另一个服务器(而不只是在不同 的操作系统之间移动)时帮助你发现错误。 • 输出表义性强的出错消息。 • 使用 Perl的内置函数,执行你要用 system函数或反引号( ``)来执行的操作。 • 将依赖系统执行的操作(文件 I /O,终端 I/ O、进程控制等)封装在函数中,检查以确保 这些操作受当前操作系统的支持。 前面两个原则你已经熟悉。在本书中,所有的代码例子均已检查了关键函数的退出状态, 而从第 8学时起,所有较大的代码例子均展示了 use strict 和警告消息。 第3个原则不能忽略,因为它是输出表义性很强的出错消息。在下面这些消息中,哪个最 有帮助呢? 显然,最后一条消息最有帮助。当你安装程序后,而且几个月(或几年)后出了问题, 最后一条消息能够说明哪个程序运行失败了( m y s c r i p t . p l) ,它想要什么( F o o f i l e . t x t ) ,它为 何运行失败(没有这个文件…) ,它在何处运行失败了(第 2 4行) 。这些信息可以帮助你迅速 排除故障。花费一点儿时间写一条很好的、表义性强的出错消息,总是值得的。 第 4 个原则意味着只要可能,你就应该使用 P e r l 。若要检索目录列表,最好只使用 $ d i r = ` d i r ` ;但是,如果该程序移植到非 Wi n d o w s系统中,那么它的运行就会失败。一种好的 解决方案是使用 < * >,而更好的解决方案则是只要可能,就使用 o p e n d i r / r e a d d i r / c l o s e d i r 函数。 无论你的程序移植到什么地方,这些解决方案都能运行。 举例说明两个操作系统之间的差别 编写“到处均可运行的”代码时遵循的最后两个原则,即“将依赖系统的操作封装在函 数中”以及“检查程序运行所在计算机上的操作系统” ,还需要作一点补充说明和演示。 当你坐在计算机面前键入 P e r l程序时,应该记住,总有一天,你的 P e r l程序有可能在另一 台计算机上运行。你可能建立下一个 Amazon.com web 站点,它可能从你的 PC移植到一台大型 Windows NT 服务器上,再移植到一群 Sun 公司 1000 UNIX 服务器上;或者你可能只有一些个 人的 CGI程序,并且更换了 Web提供商,结果发现新提供商配备的是一种不同的服务器。这些 情况经常会出现,必须加以考虑。 那么,你的程序究竟如何知道 Windows NT 与UNIX之间的差别呢?这个问题很简单。 Perl 下载 133 第11学时 系统之间的互操作性使用 有一个特殊变量 $ ^ O ,即美元符号,插入记号 ^ 和大写字母 O ,这个变量包含了程序运行时所 在的操作系统结构。例如,在 Windows和DOS 下,它包含字符串 MSWin32。在 UNIX下,它包 含你运行的 UNIX类型,如 linux,aix和solaris 等。 下面是依赖你运行的操作系统而执行的一些操作任务: • 查找关于系统配置的各种信息。 • 对磁盘和目录结构进行操作。 • 使用系统服务程序( email) 。 在下面这个例子中,可以查看一个代码段,以便找出系统上的可用磁盘空间。如果有人 想要将一个文件上载到一个服务器并且想要确定该文件是否适合在该服务器上运行,那么下 面这个例子是有用的。若要在 Wi n d o w s系统的当前目录中找出可用的磁盘空间,可以使用类 似下面的代码段: 上面这个代码段取出 @ d i r 中的目录列表的最后一行,使用正则表达式删除不包括大小 (即 bytes free 前面的数字和逗号)在内的其他信息。最后,逗号被删除,这样, $free只包含原 始的空闲磁盘空间。这种方法非常适用于 Wi n d o w s系统。对于 U N I X 系统,尤其是 L i n u x ,则 可使用下面这个代码段: 请注意上面这个代码段与前面那个代码段之间的差别。在 Windows下查找磁盘空间的实用 程序是 dir,在 UNIX 下,它是 df -k. 。df -k 输出的最后一行被分割成若干部分,第 4个域被放入 $ f r e e中。 d f的输出随着 U N I X 系统的不同而各有差异,通常情况下,报告的域的数目是不一样 的,也可能它们使用不同的顺序。你的 Perl代码只需要选择一个不同的域,就很容易得到整。 因此,现在有两种完全不同的例程可以用来确定空闲的磁盘空间。可以将它们组合起来, 并让适当的例程在每个操作系统上运行,如下所示: 134 使用第二部分 高 级 特 性 下载 这个示例程序现在已经扩展为同时包括 D O S /Wi n d o w s版本和 L i n u x版本。如果它在任何 其他操作系统的机器上运行,就会输出一条警告消息。该例程几乎已经运行结束。现在你需 要做的事情是在函数中将该例程隔离出来,使得需要的变量可以声明为专用变量,并且最后 的结果可以裁剪,粘贴到任何程序中,并在需要时随时使用。产生的结果代码是: 这时,每当你的程序需要了解空闲磁盘空间量时,只需要调用 f r e e s p a c e ( ) 函数即可,答案 将被返回。如果想在没有列出的另一个操作系统上运行该函数,就会输出一条出错消息。但 是,将另一个 UNIX型OS添加给该函数,将是很难的,你只能添加另一个子句。 11.5 课时小结 本学时你学习了如何使用系统的实用程序来进行各项操作。使用 s y s t e m 函数,可以运行 一个系统实用程序(或者一个管道程序) 。反引号( ` ` )能够运行一个系统实用程序,然后捕 获它的输出。接着,捕获的输出存放在一个变量中,供 Perl使用。open函数不仅可以打开文件, 而且可以打开程序。该程序可以用 p r i n t函数写入尖括号运算符( < >),或者从尖括号运算符中 读取数据。最后,我们介绍了将这些实用程序用于许多不同类型的操作系统的方法,但是不 必为每种系统编写不同的程序。 11.6 课外作业 11.6.1 专家答疑 问题: 如何打开既可以到达一个命令也可以来自一个命令的管道?例如: open (P , “| cmd |”) 下载 135 第11学时 系统之间的互操作性使用 这个管道似乎并不能运行。为什么? 解答: 这项操作实际上相当复杂,因为从同一个进程读取和写入数据可能导致程序“死 锁” 。这时,你的程序期望 c m d 输出某些信息,并且等待带有 < P > 的数据。同时,因为存在某 些混乱状态, cmd实际上等待你的程序输出带有 print P “...”的某些数据。事实上,如果你激 活了警告特性, P e r l 将向你报一条消息: C a n ’t do bidirectional pipe (无法执行双向管道操 “ 作) 。 ” 如果你为这种问题做好了准备, I P C: O p e n 2 模块将允许你打开一个双向开关。这些模 : 块将在第 14学时中介绍。 问题: 代码 $a=system( cmd” “ )无法按我期望的那样捕获 $a 中的 cmd的输出。为什么? 解答: 你将 s y s t e m与反引号( ` ` )的作用混淆了。 s y s t e m函数不能捕获 c m d的输出,你需 要的代码是 $a=`cmd` 。 问题: 当我在 U N I X 下运行带有反引号( ` ` )的外部程序时,没有捕获出错消息,原因何 在? 解答: 由于所有 U N I X程序,包括 P e r l 程序,都包含两个输出文件描述符,即 S T D O U T和 S T D E R R 。文件描述符 S T D O U T 用于捕获正常程序输出。文件描述符 S T D E R R 用于捕获出错 消息。反引号和带有管道的 o p e n函数只能捕获 S T D O U T 。简单的答案是使用 s h e l l将 S T D O U T 重定向到 STDERR ,然后运行下面这个命令: P e r l的FA Q (常见问题)详细介绍了这个命令和捕获命令的错误时使用的其他方法。键入 perldoc perlfaq8 ,便可打开 FAQ的有关内容。 11.6.2 思考题 1) 若要使你的程序生成的数据每次显示 1页,应使用: 2) $foo 的哪个值用于 $r=`dir $foo` 这个语句? a. $foo 的shell 的值。 b. Perl的$foo值被替换,然后运行 dir。 3) 下列操作中的哪个操作随着操作系统的不同而变更? a. 找出空闲磁盘空间的容量 b. 获取目录列表 c. 删除目录 11.6.3 解答 1) 答案是 a或b 。如果选 a,m y p r o g . p l 的所有输出均送入 m o r e。如果选择 b,那么写入文 件描述符 M 的任何数据均送入 more,以便进行分页。 2) 答案是 b。若要防止 $foo被Perl展开,你可以使用 qx`dix $foo` 。 3) 答案只能是 a 。 b 的操作可以用 g l o b , < * > 或 o p e n d i r 和 r e a d d i r 来完成。 C 可以用 r m d i r 136 使用第二部分 高 级 特 性 下载 完成。 11.6.4 实习 • 使用第 8学时中的统计函数,显示程序清单 11-1 中关于文件大小的更多的统计数据。 • 如果你拥有 UNIX系统,将你的 UNIX 的特定样式添加给 freespace()函数。使用 Linux作为 练习的开始。 ...
View Full Document

This note was uploaded on 11/27/2011 for the course CS Perl taught by Professor Guo during the Spring '09 term at Xiamen University.

Ask a homework question - tutors are online