Perl之所以能够一直在脚本程序中占有一席之地,有一些部份其实也是因为在系统管理中,Perl还能发挥着不错的效用,而且在使用上也是非常方便。它能够像shell script一样,拿了就直接用,而不需要定义一堆变量,对象,对象的方法之后才开始写程序码。你可以找到相关的模块,然后非常迅速的完成你想达到的目的。所以它保留了shell script的方便性,却又比shell script拥有更多的资源。当然优点也在某些程度上被当为缺点,例如有些人认为Perl程序码非常的不够严谨,因为相对于Java或Python,它显然太过自由了。
其实Perl对于Linux/*BSD的方便性几乎是不言可喻,很多时候在这些系统中都会预设安装了Perl,因为Perl能够相当程度的处理系统中的杂事。但是不只如此,如果你是一个系统管理员,Perl能帮你作的事情也许比你想像中的还要多。尤其当你需要对很多系统的资料进行搜寻,比对的时候,Perl就更能显示它的重要性。何况很多时候,我们可以透过日志档(log)的分析来进行系统的监控跟效率的评比,而这也正是Perl的其中一项专长。这也就对于很多系统管理的统计部份(例如MRTG跟awstats)是藉由perl来达成能做出很好的解释了。
这一章的大纲是根据Autrijus Tang在对一群准Linux系统管理员上课前,我们一起讨论出来的结果,而内容则有许多是直接使用他上课中使用的范例。这些范例在原作中以开放文本的方式释出,各位可以直接使用在测试或其他正式的产品中。
17.1 Perl在系统管理上的优势
其实就像我们前面所提的,Perl在系统管理上有着非常重要的应用跟地位,对于许多Unix-like之类的操作系统管理来说,Perl经常是他们的好帮手。可是Perl到底有甚么特别的优势呢?除了我们刚刚提到的文字比对,处理的优势外,Perl其实还有不少能够吸引人的地方。其中最特殊几点大概包括:
1. Perl的黏性特强:Perl被称为是一种黏胶程序语言,他能够用最省力的方式把各种东西绑在一起。作为一个系统管理员总是会大量接触各式各样的工具,因此需要整合这些工具的机会就非常的大,所以perl的优势在这个时候就能够容易的显现出来。
2. Perl资源丰富:我们之前多次提到的CPAN就是最丰富的数据库,不单单是网路程序,数据库处理,其实就像系统管理相关的部份,也有为数不少的模块。这些模块不但是许多系统管理员实际用来管理的工具,当然也是他们的经验。所以你看这些模块,不单单是找寻可供应用的工具,也可以藉此挖掘这些人管理系统的方式。
3. Perl的作业环境:除了Unix-like的各种操作系统能够轻易的安装,执行Perl之外,微软的Windows或是OS/2,以及苹果公司的Mac OS也都可以让Perl正常的运作。因此很多时候,当系统管理者同时必须管理一种以上的作业平台时,也能够轻易的使用相同的工具。
4. Perl几乎是一种标准:这里所谓的标准,其实是因为目前已经很多的系统管理工具都是使用Perl所开发出来的,所以如果系统管理人员如果可以拥有Perl的能力,那么自己维护或修改这些系统的可能性就可以大大增加。
17.2 Perl的单行执行模式
很多系统管理员使用Perl当然是因为Perl的顺手跟方便,就像我们说的,你总不会希望找一个文件,或置换文件的某些字串前还要先定义一大堆变量,或是先弄一个对象,然后拿来继承,再利用被继承的对象写出置换字串的对象方法。然后又没有好的正则表达式,然后你可能得用substr一个一个去找出来。最惨的可能是你写完这样的程序已经是下班时间,所以你还得加班把需要的结果搞定。而且你知道,程序写出来不一定可以那么顺利,尤其你写了那么长的程序,有bugs也在所难免,然后......。于是,下场应该是可以预料的。
虽然很多人批评Perl非常不严谨,可是换个角度看,应该是说Perl允许你「随手」写出可以解决手边工作的工具。而且Perl的「随手」还不是普通的随手。因为Perl提供单行执行模式,所以你可能可以看到这样的一行程序:
perl -MEncode -pi -e '$_ = encode_utf8(decode(big5 => $_))' *
perl -pi.bak -e "s,foo,bar," *
perl -e "$foo = 2; print $foo"
find /home/hcchien/svn/ *.txt -print | sort 如果你用Find::File::Rule来写,可能会像这样: #!/usr/bin/perl -w use File::Find::Rule; my $rule = File::Find::Rule->new; my @files = $rule->file->name( '*.txt' )->in('/home/hcchien/'); print "$_$/" for @files;
#!/usr/bin/perl -w use File::Find::Rule; my $rule = File::Find::Rule->new; $rule->file; $rule->executable; my @files = $rule->name( '*.txt' )->in('/home/hcchien/'); for my $file (@files) { open READ, $file; s/foo/bar/g while (); }
用传统的作法,我们可以这么写: open FILE, "); 倒也相当简洁,不过现在使用IO::All,就只要这么做: my $buf < io('foo.txt');
use Mail::Audit; use Mail::SpamAssassin; my $m = Mail::Audit->new( emergency => "~/emergency_mbox", nomime => 1, ); my $sa = Mail::SpamAssassin->new; $m->pipe("listgate cle") if $m->from =~ /svk-devel/; # 送到pipe $m->accept("~/perl") if $m->from =~ /perl/; # 接进特定信箱 $m->reject("no rbl") if $m->rblcheck; # 拒绝黑名单 $m->ignore if $m->subject =~ /sex/i; # 忽略信件 my $status = $sa->check($m); # 检查垃圾信 if ($status->is_spam) { $status->rewrite_mail; # 加上档头 $m->accept("~/Mail/spam"); # 收进垃圾桶 } $m->noexit(1); $m->accept("~/Mail/%Y%m"); $m->noexit(0);# 按月汇整 $m->accept; # 其余接收
if ($mail->rblcheck) { ...... }
use Mail::Sendmail; %mail = ( To => '[email protected]', From => '[email protected]', Message => "救命啊!Apache不动了!!" ); sendmail(%mail) or die $Mail::Sendmail::error;
use Mail::Bulkmail; my $bulk = Mail::Bulkmail->new( LIST => "~/listfile", # 地址清单 From => '[email protected]', # 寄件人 Subject => "System Information", # 标题 Message => '~/announcement.txt' # 内文档名 message_from_file => 1 # 从文件读取内文 ); $bulk->bulkmail() or die Mail::Bulkmail->error;
use Mail::POP3Client; my $pop = Mail::POP3Client->new( USER => "me", PASSWORD => "mypassword", HOST => "pop3.example.com", ); foreach ( 1 .. $pop->Count-1 ) { $pop->Head( $_ ) =~ /^From:/s+somebody/@example.com/ or next; open my $fh, '>', "mail-$_.txt" or die $!; $pop->RetrieveToFile($fh, $_); }
use Mail::IMAPClient; my $imap = Mail::IMAPClient->new; $imap = Mail::IMAPClient->new( Server => $host, User => $id, Password=> $pass, ) or die "无法连上主机:$host as $id: $@";
$imap->Range($imap->messages);
my $newUid = $imap->move($newFolder, $oldUid) or die "Could not move: $@/n"; $imap->expunge;
use Parse::Syslog; my $syslog = Parse::Syslog->new('/var/log/syslog'); while (my $entry = $syslog->next) { $entry->{program} =~ /sudo$/ or next; print localtime($entry->{timestamp})."/n", "$entry->{text}/n/n"; }
use Log::Dispatch; my $log = Log::Dispatch->new; # 建立记录对象 # 新增记录档 $log->add( Log::Dispatch::File->new( name => 'file', # 对象名称 min_level => 'debug', # 记录门槛 filename => '/var/log/test.log', # 记录档名 ) ); 接下来,我们把想要写入的讯息像这样的加入文件中: $log->log( level => 'alert', message => 'Strange data in incoming request' );
除错 (debug) 消息 (info) 提示 (notice) 警告 (warning) 错误 (error) 关键 (critical) 警铃 (alert) 紧急事件 (emergency)
$log->add( Log::Dispatch::Email::MailSend->new( name => 'email', # 对象名称 min_level => 'emergency', # 记录门槛 to => [ '[email protected]' ], # 收件地址 subject => 'HELP!!!', # 邮件标题 ) );
use Net::SMS; my $df = `df -h`; # 取得硬盘配置资讯 $df =~ /-/d/ or exit; # 若没有数字是负的就直接离开 my $sms = Net::SMS->new; # 简讯对象 $sms->accountId("123-456-789-12345"); # 帐号 $sms->accountPassword("mypassword"); # 密码 $sms->sourceAddr("0928-000-000"); # 来源 $sms->destAddr("0928-999-999"); # 目的 $sms->msgData("HARD DISK FULL:/n$df"); # 讯息 $sms->submit; # 送出简讯 $sms->success or die $sms->errorDesc; # 侦测错误
#!/usr/bin/perl -w use strict; use Template; use IO::All; use Mail::MboxParser; my $dir = io('/var/mail'); # 准备逐个检查信箱 my %all; while (my $io = $dir->next) { # 一个一个看信箱的信件数目 if ($io->is_file) { # 只检查文件 eval { # 避免因为某些原因中断 my $mb = Mail::MboxParser->new("$io", newline => '#DELIMITER'); %all{$io} = $mb->nmsgs; # 把结果放入杂凑 } } } my $config = { INCLUDE_PATH => '/search/path', POST_CHOMP => 1, }; my $template = Template->new($config); # 建立新的模板对象 my $vars = { messages => /%all }; my $input = 'report.html'; my $output; $template->process($input, $vars, $output) # 处理模板内容 || die $template->error(); print $output;
use GD::Graph::bars3d; my $graph = GD::Graph::bars3d->new(800, 600); # 新增柱状图 my @files = ; my $image = $graph->plot([ # 订出横座标,纵座标内容 [map /(/d+)/./g, @files], [map -s, @files], ]) or die $graph->error; open my $fh, '>', '3.png' or die $!; print $fh $image->png; # 储存影像
2. 承上题,统计当月每天的退信数字,并且画成长条图。
Jun 3 00:00:46 dns2 postfix/smtpd[71431]: D988D6A: reject: RCPT from smtp2.wanadoo.fr[193.252.22.29]: 450 < [email protected]>: User unknown in local recipient table; from=<> to=proto=ESMTP helo=