考虑到公司环境必须先rsa_auth再su的问题,一般的pssh啊mussh啊sshbatch啊,都不能直接用,决定把上篇脚本里的相关部分抽出来成为一个模块,借机学习一下package和bless的简单概念: ```perl #包名,如果做pm的话,必须和(.*).pm的名字一样 package raocl; use Parallel::ForkManager; use Expect; #Exporter模块是perl提供的导入模块方法的工具 use base ‘Exporter’; #Exporter有两个数组,@EXPORT里存的是模块的sub,@EXPORT_OK里存的是模块的var; #使用模块时只能调用这些数组里有定义的东西 our @EXPORT = qw/new cluster/; #一般模块都有一个new方法来进行初始化定义 sub new { #所有sub传入的第一个参数都是本身,所以要先shift出来,然后才是脚本显式传入的参数 my $class = shift; #将参数转成哈希方式,并返回一个引用; #正规做法应该在这里指定一些必须要有的参数,比如passwd => %args{‘passwd’} || ‘123456’ my $self = {@_}; #bless上面返回的哈希引用到自己,再传递出去;以后在这之外的地方,使用被bless过的$self时自动就关联上new里的数据了。 #这里我写的极简单,看比较正式的模块写发,这里对$class还要用ref();判断是不是引用等 return bless $self,$class; }

sub cluster { #这里的$self就是上面被bless过的了 my ($self, $command) = @; my %remote_result; my $pm = Parallel::ForkManager->new( $self->{fork}, ‘/tmp/’); $pm->run_on_finish ( sub { my ($pid, $exit_code, $ident, $exit_signal, $core_dump, $reference) = @; if (defined($reference)) { my @data = @$reference; $remote_result{“$data[0]”} = $data[1]; } else { print qq|No message received from child process $pid!\n|; } } ); #直接使用bless过的$self解引用出来的hosts列表 foreach my $remote_host (@{$self->{hosts}}) { $pm->start and next; #使用bless过的$self的sub完成expect功能 my @check = ($remote_host, $self->pexpect($remote_host, $command)); $pm->finish(0, \@check); } $pm->wait_all_children;

return %remote_result; }

sub pexpect { #还是同样的$self,然后才是上面调用时传递的$host和$shell my ($self, $host, $shell) = @_; #使用new里提供的passwd my $password = $self->{passwd}; my $exp = Expect->new; $exp = Expect->spawn(“ssh -l admin -i /usr/local/admin/conf/id_rsa -o ConnectTimeout=5 $host”); $ENV{TERM}=”xterm”; $exp->raw_pty(1); #使用new里提供的开关 $exp->exp_internal(“$self->{debug}”); $exp->log_stdout(“$self->{output}”); $exp->expect(2,[ ‘$’, sub { my $self = shift; $self->send(“su -\n”); } ], [ ‘(yes/no)\?’, sub { my $self = shift; $self->send(“yes\n”); exp_continue; } ] );

$exp->expect(2, [ ‘Password:’, sub { my $self = shift; $self->send(“${password}\n”); exp_continue; } ], [ ‘#’, sub { my $self = shift; $self->send(“${shell}\n”); } ] ); $exp->send(“exit\n”) if ($exp->expect(undef,’#’)); #因为shell命令执行的输出可能有滞后,所以将前后都输出 my $read = $exp->before() . $exp->after(); $read =~ s/[.+\@.+]//; $exp->send(“exit\n”) if ($exp->expect(undef,’$’)); $exp->soft_close(); return $read; } #package结尾,必须return一个1,原因未知…… 1; 使用如下: perl#!/usr/bin/perl -w #可以在/usr/lib/perl5下,也可以在pl脚本的同目录下 use raocl; #从文件中读取host列表为数组 open FH, “./list”; my @hosts = ; #使用new初始化,传递host列表的引用给函数 $raocl=new raocl(hosts=>\@hosts, fork => '10', output => 0, debug => 0, passwd => '123456',); %result = $raocl->cluster("$shell"); foreach my $host (keys %result) { print $host."\t".$result{$key},"##############\n"; };```