6. Perl的基本I/O操作
在开始处理字符串之前或许我们应该先看看Perl怎么和外部数据进行交换。前面我们使用了<STDIN>来接收程序键盘输入,这实际是是用到了Perl的文件句柄和<>操作符。
6.1 <>操作符
<>操作符从文件句柄中读出一行,如果把文件句柄比成一个由每一行组成的数组(array),那么<>操作符就类似于pop/shift这样的函数。看下面的例子:
[root@Ora01 codes]# more p18.pl
use strict;
my @lines = ();
print("Pls input 3 line in STDIN( Keyboard ): \n");
for(my $i=0;$i<3;$i++){
$lines[$i] = <STDIN>; #STDIN是Perl预定义的句柄,而且已经预打开。用于接收标准输入(键盘输入)
print("Your input is :\n");
foreach my $line(@lines){
print("$line");
如果在<>操作符中没有指定文件句柄会怎么样呢?<>会作为特殊行输入操作。文件句柄由客户自己选择,看看下面的例子:
while(defined(my $line=<>)){ #输入结束时候,<>操作符的结果为undef。
print("$line");
#<>操作符查看@ARGV来决定使用那些文件。如果为空则使用标准输入(STDIN)
[root@Ora01 codes]# perl p19.pl
[root@Ora01 codes]# perl p19.pl "/etc/hosts" #$ARGV[0] = 文件名
# Do not remove the following line, or various programs
# that require network functionality will fail.
127.0.0.1 localhost.localdomain localhost
10.10.10.120 Ora01.LiXiang Ora01
6.2 文件句柄
文件句柄(filehandle)是给I/O(如文件,设备,网络套接字或者管道)起的一个名字。这可以分清楚我们正在和哪个文件或者设备通讯,同时隐藏了如缓冲等的复杂性。文件句柄能帮助我们容易地从不同的地方接收输入,并输出到不同的地方。
文件句柄的命名规则和Perl中其它的标识符一样(由字母,数字,下划线组成,但不能数字开头);由于没有前缀,这可能和现在或者将来的保留字,标签混淆。所以取一个不容易混淆的名字很重要(和其它的标识符一样,大小写敏感)。Larry推荐文件句柄的所有字母均大写,这有利于阅读。虽然句柄名称大小写Perl都是被接受的。Perl自身预定义有六个文件句柄:STDIN, STDOUT, STDERR, DATA, ARGV, ARGVOUT。虽然可以任意给文件句柄命名,但用户定义的句柄名称不能选择上面六个,除非你要用的就是Perl预定义的那六个句柄。
当我们可以使用一个句柄时候,需要使用open函数打开一个句柄。Open函数需要至少两个参数:文件句柄和你希望与句柄关联的文件名。(上面提到的Perl预定义的句柄是已经预先打开的,所以不需要open就能直接使用。)和C++中的数据流一样,我们需要用close函数来关闭文件句柄释放资源,当我们不需要的时候。看下面一个例子:
#使用文件句柄打开/etc/hosts文件,并打印其内容
[root@Ora01 codes]# more p20.pl
use strict;
my $filename = '/etc/hosts';
open(FH_HOSTS,$filename);
my @lines = <FH_HOSTS>;
close(FH_HOSTS);
print @lines;
Open函数还可以创建用于不同用途(输入,输出,管道)的句柄。要创建不同用途的句柄,可以通过在文件名前面加前缀>或者>>。
Open(FILEHANDLE, $filename); #or open(FILEHANDLE,”<$filename”); open for input,
Open(FILEHANDLE,”>$filename”); #open for output
Open(FILEHANDLE,”>>$filename); #open for appending
Open(MYSTDIN,’-‘); # = <STDIN>;
Open(MYSTDOUT,’>-‘); #Open standard output.
6.3 外部程序的调用
虽然Perl对外部操作系统程序的调用不属于I/O操作。脚本在很多时候都需要调用操作系统的一些程序(命令)来自动完成一些操作。或者Perl脚本用到一些外部程序的输出。我们来看看Perl调用外部程序的一些用法。
我们可以通过system或者exec这两个Perl内嵌函数来对外部程序进行调用,这和C语言的做法很像。system和exec的区别是system等待外部命令执行返回再继续执行,而exec不等外部程序执行返回(要看具体区别可以使用perldoc Cf查看文档)。
#函数原型: system LIST
# my @mycmd = ("netstat","-r");
#my $mycmd = 'netstat -r'; 参数是标量这也是可以的,应该是在某个地方自动转换成了列表了
my @mycmd = ("D:\\Program Files\\PuTTY\\putty.exe"); #可以带路径
if(system(@mycmd)){
print("Ok.");
}else{
print("Failed.")
system和exec函数返回值是布尔类型表示命令执行成功或失败,而调用的程序的输出是传到Perl标准输出(显示屏)上的。有时需要将其结果作为字符串保留下来以便进一步处理。这时我们可以使用通过使用反引号(`)做到,就像shell语言一样:
#!perl
my $mycmd = "ipconfig";
my @cmdout = `$mycmd`;
foreach (@cmdout){
print($_);
如果我们给open函数的第二个参数后缀或者前缀一个管道符号(|),那么Perl会给我们打开一个管道而不是一个文件。然后“|”剩下的就成为一条命令,这条命令会被Perl解释成一个进程,而我们则可以从这条命令中输入或者输出数据流。
[root@demo01 codes]# vi p01.pl
#!/usr/bin/perl
use strict;
my @output = `ps -ef`;
open my $row(GREP,"|grep ora");
foreach (@output){
print(GREP $row."\n");
Close(GREP);
#管道符在命令前面说明命令从匿名管道读取输出。程序其实等效于命令ps Cef|grep ora
#如果写成open(GREP “grep ora|”);这运行会报错: grep: (standard input): Input/output error
#并不是所有命令都能从匿名管道里读取数据的 比如说:
[root@demo01 codes]# echo "/etc/hosts"|ls -l
total 8
-rw-r--r-- 1 root root 87 Dec 7 23:45 p01.pl
-rw-r--r-- 1 root root 6 Dec 7 22:55 test.txt
#并没有变成想要的ls Cal /etc/hosts
#用open(FD,”|ls Cal”);的话其实不管往pipe里输入什么,ls Cal 都是返回当目录。
#另外一个例子是从管道里读出数据,命令在管道符后面。
[root@demo01 codes]# vi p01.pl
#!/usr/bin/perl
use strict;
open (FH,"ls -al|");
print(<FH>); #将ls Cal的输出打印出来。
close(FH);
我对Unix进程间的管道通信(IPC)没什么概念,单从使用外部程序调用来看,如果只是获取外部程序的输出,那么只要给open函数的第二个参数传入要执行的命令,后缀加管道符(your_command|) 就行了(也就是读管道中的数据),至于使用pipe的方式有什么好处,书中告诉我们,如果是用反引号(``),我们在命令执行完之前是得不到任何输出的,而管道方式则可以在程序执行过程中就获得程序输出。