Perl与系统管理

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 => $_))' *

执行完这一行之后,你该目录下的Big5文件就会全部都变成UTF8了。这实在非常神奇,不是吗?其实不只这样,你还可以利用一行的程序把文件中的某些字串一次换掉。就像这样:


perl -pi.bak -e "s,foo,bar," *

这样一来,所有文件中的foo就全部被换成bar了。希望你没有继续想像如果你手动替换或是正在用其他结构严谨的程序语言在进行中。当然,如果你现在还在这里,我们就利用那些坚持一定要有足够完整的程序架构才能执行的家伙还在奋斗的时间,来看看怎么执行单行的Perl吧。
首先,最基本的就是"-e"这个参数了,你可以使用perl -e来执行一行程序。不过请注意,我说的是一行程序,而不是一个叙述句,所以你当然可以这么写:


perl -e "$foo = 2; print $foo"

接下来,虽然只是一行的Perl程序,可是你还是可以使用Perl的其他各式各样模块,如果没有办法使用模块,那单行的执行模式大概也没有人愿意使用了吧!所以我们使用了"-M"的选项来指定所要使用的模块,就像前面的第一个例子中,我们用了"-MEncode"来指定使用Encode这个模块。
接下来,我们要让这一行程序能对所有我们指定的文件工作,所以我们使用了"-p"这个选项这个选项允许使用者以循环的方式来执行这一行程序,在这里也就让我们可以使用*这个万用字符来指定所有的文件。接下来,我们看到另一个选项是"-i",这是让perl会自动帮你进行备份,免得你对文件进行操作之后,结果非常不满意,却还得手动改回来。所以当我们指定了"-i.bak"就是让所有被修改的文件在修改前就先备份一个副档名为.bak的文件。不过这是小写i,如果你用了大写的I,那意义可就大不相同了,"-I"让你可以指定@INC的内容。

17.3 管理文件

系统管理一开始大概都要面对一大堆的文件吧,而基本的文件管理其实还没有太过复杂,只要你的shell够熟,几乎也可以应付很多状况。例如你可以轻松的利用find找到你要的文件,甚至让他们排序,就像这样:


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;

Perl的写法不但比较长,比较复杂,而且速度比起你在shell底下真是慢了好几倍。暂时看起来,Perl在这样简单的状况下实在不特别好用。可是别忘了,像这样简单的状况,每个人都习惯随手就用shell解决,可是如果情况稍微复杂一点呢?比如我想找出文件状态是可执行的.txt文件,那么你应该怎么做呢?接下来,我想把这些文件的可执行模式取消,然后也许再修改某些内容......。如果只是单一内容,shell确实非常轻巧,快速,可是一但我们要把一堆操作集合在一起时,你就会发现perl有甚么过人之处了。


#!/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 (<READ>); }

当然,你还可以对这些文件做其他的操作,例如每个文件都插入一列新的资料等等。这时候,有另外一个模块就显得非常有用了,这么模块就是IO::All。还记得我们之前怎么读入一个文件的内容吧?IO::All现在可以让你非常简单的控制文件,我们来比较看看:


用传统的作法,我们可以这么写: open FILE, "<foo.txt"; $buf.=$_ while (<FILE>); 倒也相当简洁,不过现在使用IO::All,就只要这么做: my $buf < io('foo.txt');

不过我们并不打算在这里深切的介绍IO::All这个模块,因为我们还有其他更重要的事情要做。

17.4 邮件管理

接下来让我们来看看作为伺服器的一个重要工作,也就是关于Mail的管理。也因为对于邮件的管理需求其实非常的大,所以其实相关的管理工具也不在少数。例如可以过滤广告信件,发送大量信件或是寄发一般的通知信件等等。不过由于某些工作的特殊性质,使得Perl成为这些工作中非常能够胜任重要工具,这些工作中当然有不少是字串内容的分析,而最具代表性的也就是广告信的过滤了。

17.4.1 Mail::Audit + Mail::SpamAssassin

这是一个非常具有杀手级实力的一个Perl模块,如果你已经是一个现任的网路管理人员,整天听到你所管理的邮件伺服器中不断传来广告邮件,而且你还不知道这个模块,那么应该先去CPAN上搜寻Mail::SpamAssassin这个模块。然后你还可以搭配Mail::Audit这个模块。这里的例子是许多人使用Mail::Audit跟Mail::SpamAssassin时经常会作为过滤信件第一关的检查工具:


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; # 其余接收

其实如果利用Mail::Audit,还可以直接套用Mail::Audit::MAPS。因为这样就可以直接把一些已经被列为黑名单的寄件者先排除在外了,而且使用上也没有甚么太大的差别。其实你只需要多做一个判断:


if ($mail->rblcheck) { ...... }

另外,Mail::Audit还有一个常用的外掛程序则是Mail::Audit::KillDups。他可以帮你删除一些ID重复的信件,其实这也是可能的广告信来源之一,所以你可以又事先过滤掉一些垃圾邮件。其实不只这些,Mail::Audit的外掛程序种类之多,实在让人觉得有趣,我自己另一个常用的是Mail::Audi::PGP,不过这就未必适合所有人了。
至于Mail::SpamAssassin,除了搭配Mail::Audit之外,其实也有命令列程序。透过命令列程序,你可以设定自己的黑名单(blacklist)跟白名单(whitelist),也可以透过手动训练的方式,来增进SpamAssassin的准确率。

17.4.2 Mail::Sendmail 与 Mail::Bulkmail

另外,perl也可以用非常方便的方式来传送mail。虽然你在系统管理时,如果需要寄发mail,可以方便的使用sendmail来传送。可是其实有些时候你会在系统中定时执行一些程序,或许是进行系统的意外检查,或许是做定时的工作。那么你可能会希望这些工作如果发生意外,你可以很快的收到电子邮件通知。这时候Mail::Sendmail就变得很有用了。


use Mail::Sendmail; %mail = ( To => 'yourmail@hostname.com', From => 'mail@server.com', Message => "救命啊!Apache不动了!!" ); sendmail(%mail) or die $Mail::Sendmail::error;

各位大概都听过「自相矛盾」的成语故事吧?而且千万别以为那是书上拿来骗小孩的故事,因为真实的就发生在perl的模块中。我们之前介绍了目前几乎被公认最好阻挡广告信的模块:SpamAssassin,可是现在我们也要介绍另一个被认为发广告信的强力模块,也就是Mail::Bulkmail。


use Mail::Bulkmail; my $bulk = Mail::Bulkmail->new( LIST => "~/listfile",   # 地址清单 From => 'admin@bulkmail.com', # 寄件人 Subject => "System Information", # 标题 Message => '~/announcement.txt' # 内文档名 message_from_file => 1 # 从文件读取内文 ); $bulk->bulkmail() or die Mail::Bulkmail->error;

在这里,你只需要把要一次传送的一大堆邮件地址逐行放进一个纯文本文件。Mail::Bulkmail就会帮你读出这些地址,而且发送邮件。至于内文,你虽然可以直接写在程序中,可是更有弹性的方式也是可以利用文字档来读入邮件内容。这样一来,如果你的邮件是每周定时发送,那么你只需要修改传送名单跟邮件内容的文件就可以轻松的让程序替你完成其他工作了。

17.4.3 POP3Client 及 IMAPClient

有些时候,你可能有一些帐号是专门用来处理一些特定的工作,那么其实这些送到该帐号的信件未必需要由一个特定的人去收,而可以利用程序去进行处理。这个时候,你可以使用Mail::POP3Client这个模块来读取这些邮件。我们先用一个简单的例子来看看这个模块的用法:


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, $_); }

这个程序可以让某个人寄来的信都被备份到一个特定的文件中,其实主要的就是透过邮件档头中的寄件者进行比对。所以如果你可以针对主题进行比对,就可以对邮件进行分类。当然,如果你想出其他更好用的邮件过滤演算法,也许可以从这里开始进行实做(虽然我们并不建议你这么做)。其实一但可以直接取出邮件中的每一封邮件,我们可以做的应用就非常的广泛了。而有了POP3Client,好像也少不了IMAPClient。我们还是先来看另一个例子吧:


use Mail::IMAPClient; my $imap = Mail::IMAPClient->new; $imap = Mail::IMAPClient->new(    Server => $host, User => $id, Password=> $pass, ) or die "无法连上主机:$host as $id: $@";

使用IMAPClient,也因为邮件伺服器种类的特性差异,让它提供更多的操作方式给使用者。最基本的比如IMAPClient的Connected跟Unconnected,另外可以透过Range来选定某一个特定范围的邮件。例如:


$imap->Range($imap->messages);

其中$imap->messages会取得目前所在文件夹的所有邮件,然后透过$imap->Range()来把这些邮件设定成要操作的邮件范围。不过你也可以在$imap->Range()中使用逗点分隔来指定某几封特定的信件。另外,如果你考虑搬移信件,你可以使用move来进行,其实非常方便,就像这样:


my $newUid = $imap->move($newFolder, $oldUid)    or die "Could not move: $@/n"; $imap->expunge;

另外,还有一些常用的方法例如delete_messages和restore_messages就分别是用来删除邮件跟回覆被删除的邮件。或是你可以用search来搜寻邮件。其实IMAPClient非常的强大,确实需要根据自己的需求去研究相关的文件才能确实掌握。

17.5 日志档

作为一个系统管理,能确实掌握每日的记录档确实是非常重要的工作。不过如果你进入/etc/log文件夹,就会发现里面的文件其实是相当多的。也就是说,如果你身为一个系统管理员,每天要注意这些文件中有没有异常,如果你还在使用传统的工人智慧,那么每天进行这样的工作就要浪费许多时间。因此要希望能够在这部份节省更多的时间来从事其他的管理工作,我们可以使用一些工作来简化这些日常的程序,Parse::Syslog就是其中之一。
我们先来看看一个简单的例子,来了解Parse::Syslog怎么帮我们处理这些日志文件。


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"; }

其实程序并不困难,首先我们设定要处理的文件,在这里我们针对"/var/log/syslog"这个文件来检查。接着Parse::Syslog传回一个对象,也就是$syslog。接下来我们使用next这个对象操作方法逐行处理这个文件,在文件结束之前,$syslog->next都会传回真值,因此让我们可以一行一行来进行比对的工作。我们试图找出日志档中关于有使用者使用sudo这个指令,所以我们使用了正则表达式。
接下来,Parse::Syslog可以帮你取得事件发生的时间,所以当我们比对成功之后,就可以印出使用者使用sudo这个指令的时间。
所以如果我们一次把所有要监视的文件内容利用Parse::Syslog设定好,那么其实以后的日子就会轻松愉快许多了。当然,如果你撷取出这魔一堆文件,那么要把这些让人需要特别注意的记录进行处理,才能方便管理者阅读或检查,这时候可以使用另一个模块来进行。
Log::Dispatch是不错的选择。你只要新建立一个相关的对象,并且指定要写入的档名,那么可以依照记录的等级来将相关的讯息写入文件中。


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 => [ 'admin@example.com' ], # 收件地址 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; # 侦测错误

我们使用Net::SMS可以直接发送简讯,在这个例子中,我们使用shell指令去检查磁盘空间。并且当发现磁盘空间不足时就发出手机简讯来警告系统管理人员。虽然你写了这些程序之后,未必就可以获得老闆的赏识而加薪,不过我想让老闆少点机会找你麻烦应该还算是一项大利多吧!

17.6 报表

身为系统管理人员,尤其当你是在企业内部进行系统管理时,最容易遭到忽略。因为当大家系统非常顺畅时,几乎没人会归功于系统管理员的认真。于是要怎么拿出实际的内容来说服其他的人,这也算是系统管理人员的重大业务之一了吧!很多人使用MRTG(注一)来监测网路流量,使用awstats(注二)来看网站的各式数据。
可惜的是大多数的人对这些报表并不太有兴趣,除非你管理的是一个(或一堆)网站,而且公司的业务也就是经营这些网站。既然如此,那么一个系统管理员有些时候其实还是要呈现出自己优良的管理绩效,在没有现成的套装工具下,要怎么样快速的建立出漂亮的报表其实也是不小的学问。
如果你需要的是文字的报表,那么我们前一章使用的Template就非常适合,你可以利用Template::Toolkit画一个HTML的报表。


#!/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;

这时候,你可以取得一个含有所有信箱邮件个数的一个杂凑变量(我们使用了Mail::MboxParser)。如此一来,你只要弄一个漂亮的模板,就可以让系统动态把资料填入,随时可以监控目前大家信箱内的邮件数目了。不过这样其实还没结束,因为很多老闆或主管对于文字的接受度总是比较低,所以如果你有漂亮的报表,那么给他们的印象应该也会随之提高。这时候,你也许可以认真考虑使用GD::Graph这个模块。这个模块可以让你轻松的画出漂亮的统计图表,你可以根据资料的属性以及需求的差异,画出例如圆饼图,曲线图,柱状图等等,如果你还想更绚丽,GD::Graph还可以画出立体的3D图形。
我们用另一个例子来看看怎么使用GD::Graph吧:


use GD::Graph::bars3d; my $graph = GD::Graph::bars3d->new(800, 600); # 新增柱状图 my @files = </var/log/maillog.*.bz2>; 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; # 储存影像

我们取得了每次的电子邮件日志档的,然后根据这些文件的大小进行统计。这时候,我们只需要订出横座标跟纵座标的内容,交给GD去画就好了,你当然也可以定时的要求程序帮你画出某些统计图表。很有用吧!你可以定时交出漂亮的工作报表,而且还是由电脑自动产生。

利用Perl来协助系统管理其实还算是非常方便的,何况已经有许多的系统管理员早就在做这些工作,也因此我们有很多方便的工具可以使用。这完全让我们省下许多时间,尤其当更多的系统管理员每天都花许多时间在这些繁琐而且又没有变化的工作上。

习题:
1. 找出maillog中被reject(退信)的资料,也就是找到日志档中以reject标明的内容。例如:

Jun 3 00:00:46 dns2 postfix/smtpd[71431]: D988D6A: reject: RCPT from smtp2.wanadoo.fr[193.252.22.29]: 450 < fnatterdobkl@hcchien.org>: User unknown in local recipient table; from=<> to=<fnatterdobkl@hcchien.org> proto=ESMTP helo=<mwinf0203.wanadoo.fr>
2. 承上题,统计当月每天的退信数字,并且画成长条图。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值