Perl语言入门笔记 第十六章 进程管理
=pod 第十六章 进程管理 身为程序员最棒的一面,就是能运行别人的程序,不必自己动手去写。 在perl里有一句话叫 “办法不止一种” system函数: 在perl中,启动子进程最简单的方法是用system函数,例如从perl调用Unix的date命令,需要告诉system要运行的外部程序的名字: system 'date'; date运行时,创建了一个子进程 system 'ls -l $HOME'; 利用shell来启动后台进程, system "long_running_command with parameters &"; &使该条命令后台运行,此处可以用于需要长时间运行的程序 system 'for i in *; do echo == $i ==; cat $i; done'; 该条指令会将当前目录中每个文件的文件名及其内容显示出来。 避免使用Shell: system操作符也可以用一个以上的参数来调用,如此一来,不管你给的文本有多复杂,都不会用到shell?????,做法如下: my $tarfile = 'something*wicked.tar'; my @dirs = qw(fred|flintstone <barney&rubble> betty); system 'tar', 'cvf', $tarfile, @dirs; system操作符的返回值是根据子进程的结束状态来决定的,在unix里,退出0代表正常,非零退出值则代表有问题: unless (system 'date') { #返回0的话就代表执行成功 print "We gave you a date, ok!\n"; } 环境变量: 现在需要运行系统的make程序,进而运行其他程序,并且想以你的私有目录作为寻找命令的首选位置,假如还要禁用IFS环境变量,以免make或者其后的命令做出不正常的举动,like this: $ENV{'PATH'} = "/home/rootbeer/bin:$ENV{'PATH'}"; delete $ENV{'IFS'}; my $make_result = system 'make'; 修改从父进程继承的环境变量并不能影响shell或者其他父进程。 exec函数: exec函数导致perl进程自己去执行任务, 例如:要运行/tmp目录下的bedrock命令并带上-o args以及程序本身所调用的参数: chdir '/tmp' or die "Cannot chdir /tmp:$!"; exec 'bedrock', '-o', 'args1', @ARGV; perl程序的主要功能是为另一个程序的运行设定运行环境,你可以预先修改环境变量,修改当前的工作目录,修改默认的文件句柄等等。 $ENV{PATH} = '/bin:/usr/bin'; $ENV{DEBUG} = 1; $ENV{ROCK} = 'granite'; chdir '/user/fred'; open STDOUT, '>', '/tmp/granite.out'; exec 'bedrock'; 用反引号捕获输出结果: 无论用system还是exc,所执行命令的输出都会送往perl的标准输出,有时候我们感兴趣的是将输出结果捕获成字符串,以便后续处理,只要用反引号代替单引号或双引号就可以了。 my $now = `date`; foreach (@function) { $about{$_} = `perldoc -t -f $_`; } qx也可以使用于捕获输出变量,主要是为了防止输出特殊字符 qx'echo $$'; 不捕获输出时能不用反引号就不用反引号,可以使用system和exec 标准输出和错误输出可以通过perl的合并信息,来输出 如:my $output_with_errors = `frobnitz -enable 2>&1`; 我们日常执行的命令大豆不会使用标准输入,所以没有这方面的问题,但是,如果date命令询问你要使用的时区(正如我们之前假设的), 这样就会有问题,因为提示文字"which time zone"会被送至标准输出,成为被捕获内容的一部分,然后date会试着从标准输入读取数据,由于用户根本看不到提示文字,所以他不知道该输入数据!没多久,用户就会打电话给你,说你的程序卡住了 因此,请勿使用会读取标准输入的命令,如果你不太确定它是否会从标准输入读取数据,请将标准输入重定向为从/dev/null读取数据,如下所示: my $result = `some_questionable_command arg arg argh </dev/null`; 这样,子shell就会将输入重定向到/dev/null,接着再执行那个交互式命令,这样就算它要求输入,也只会读到文件结束符。 从列表上下文中使用反引号: 如果命令会输出很多行,那么在标量上下文中使用反引号会得到一个很长的字符串,其中包含换行符,不过,如果是在列表上下文使用同样的反引号,则会返回输出字符串按行拆分的列表。 比如,unix下的who指令: merlyn tty/42 dec 7 19:41 rootbeer console dec 2 14:35 rootbeer tty/12 dec 6 23:00 标量上下文: my $who_text = `who`; my @who_lines = split /\n/, $who_test; #自行拆开 列表上下文: 自动拆分成多行 my @who_lines = `who`; =号是正则表达式,=~是绑定操作符 用IPC::System::Simple执行外部进程: IPC::System::Simple不是perl自带的模块,需要从CPAN下载 可以代替system函数,但是执行更加健壮 use IPC::System::Simple qw(system); my $tarfile = 'something*wicked.tar'; my @dirs = qw(fred|flintstone<barney&rubble>betty); system 'tar', 'cvf', $tarfile, @dirs; 他提供了一个systemx函数,在执行外部命令时不会通过shell调用,所以不会碰到shell导致意外的情况。 如果要捕获外部命令的输出,只需要办system或systemx改成capture或者是capturex就可以了,他们的作用就好像使用反引号一样。 my @output = capturex 'tar', 'cvf', $tarfile, @dirs; 好模块。。。。。。。。。 但是shell里面的命令调不到呀 通过文件句柄执行外部进程: 要启发并发运行的子进程,请将命令放在open调用的文件名的部分,并且在它的前面或后面加上竖线,也就是管道符号,因此,有人将这种调用方式就做“管道打开”,在两个参数的行驶中,管道符号安放在要执行的命令的开头或者结尾: open DATE, 'date|' or die "Cannot pipe from date:$!"; open MAIL, '|mail merlyn' or die "Cannot pipe to mail:$!"; '|'在指令的左边或右边代表输出还是输入 读取文件句柄:-| 写入文件句柄:|- 句柄需要关闭 如果子进程不时有数据要送给父进程的话,就必须用管道了 反引号和|-的输出结果的先后顺序 用fork进行深入和复杂的工作: system 'date'; 用fork可写成: defined(my $pid = fork) or die "Cannot fork:$!"; unless($pid) { #能运行到这里的是子进程 exec 'date'; die "Cannot exec date:$!"; } waitpid($pid, 0); #能运行到这里的是父进程 发送及接收信号: kill 2, 4201 or die "Cannot signal 4201 with SIGINT:$!"; or: kill 'INT', 4201 or die "Cannot signal 4201 with SIGINT: $!"; or: kill INT => 4201 or die "Cannot signal 4201 with SIGINT: $!"; 判断信号是否还存在,只是判断,并不是杀死 unless(kill 0,$pid) { warn "$pid has gone away!"; } 清理垃圾的函数: my $temp_directory = "/tmp/myprog.$$";#在这个目录下创建文件 mkdir $temp_directory, 0700 or die "Cannot create $temp_directory: $!"; sub clean_up { unlink glob "$temp_directory/*"; rmdir $temp_directory; } sub my_int_handler { &clean_up(); die "interrupted, exiting...\n"; } $SIG{'INT'} = 'my_int_handler'; ... ... &clean_up(); #。。。。。真正清理的部分 只是终止当前程序,返回原来程序,只要在信号处理子程序中设置一个标记,然后再每行处理结束时检查它既可: my $int_count = 0; sub my_int_handler{$int_count++}; $SIG{'INT'} = 'my_int_handler'; ...; while(<SOMEFILE>) { ...;#一般要花几秒时间来执行的操作 if($int_count) { #看到进来的中断信号了 print "[processing interrupted...]\n"; last; } } \N:表示除换行符之外的所有字符 =cut
不负自己