如何异步运行多个perl打开命令并按顺序显示输出

时间:2013-04-05 04:23:18

标签: perl asynchronous

我正在尝试异步运行多个SSH命令到多个服务器,我想从命令中捕获输出并按顺序显示它们。 要投入额外的曲线球,我希望pid3只在pid2完成后运行,而pid4在前三个命令完成后运行。如何最好地完成这项工作?

示例:

// $pid1 and $pid2 should run asynchronously
my $pid1 = open(my $SSH1, "|ssh -t -t runuser\@$server{'app'} 'sudo chef-client'");

my $pid2 = open(my $SSH2, "|ssh -t -t runuser\@$server{'auth'} 'sudo chef-client'");

// This command should wait for $pid2 to complete.
my $pid3 = open(my $SSH3, "|ssh -t -t runuser\@$server{'auth'} \"sudo -- sh -c '$update_commands'\"");

// This command should wait for $pid1-3 to complete before running.
my $pid4 = open(my $SSH4, "|ssh -t -t runuser\@$server{'varn'} \"sudo -- sh -c '$varn_commands'\"");

3 个答案:

答案 0 :(得分:1)

到目前为止,我的(有些粗糙的)解决方案。我觉得在Perl中可能有更优雅的方法来处理这个问题,但这可能会完成工作:

# Silence all non-error output from the commands on first 2 servers:
my $pid1 = open(my $SSH1, "|ssh -t -t runuser\@$server{'app'} 'sudo chef-client > /dev/null'");

my $pid2 = open(my $SSH2, "|ssh -t -t runuser\@$server{'auth'} 'sudo chef-client > /dev/null'");

if ($pid1) {
    print "Connecting to $server{'app'}: chef-client";
    while ( <$SSH1> ) {
        print $server{'app'};
        print $_;
    }
}
close $SSH1 or die $!;

if ($pid2) {
    print "Connecting to $server{'auth'}: chef-client";
    while ( <$SSH2> ) {
        print $server{'auth'};
        print $_;
    }
}
close $SSH2 or die $!;

# Run pid3 once pid2 is closed
my $pid3 = open(my $SSH3, "|ssh -t -t runuser\@$server{'auth'} \"sudo -- sh -c '$update_command'\"");
if ($pid3) {
    print "Connecting to $server{'auth'}: $update_command";
    while ( <$SSH3> ) {
        print $_;
    }
}
close $SSH3 or die $!;

# Run pid4 after all previous commands have completed.
my $pid4 = open(my $SSH4, "|ssh -t -t runuser\@$server{'varn'} \"sudo -- sh -c '$varn_command'\"");
if ($pid4) {
    print "Connecting to $server{'varn'}: $varn_command";
    while ( <$SSH4> ) {
        print $_;
    }
}
close $SSH4 or die $!;

答案 1 :(得分:1)

Forks::Super处理所有这些要求:

use Forks::Super;

# run  $command1  and  $command2 , make stderr available
my $job1 = fork { cmd => $command1, child_fh => 'err' };
my $job2 = fork { cmd => $command2, child_fh => 'err' };

# job 3 must wait for job 2. Collect stdout, stderr
my $job3 = fork { cmd => $command3, depend_on => $job2, child_fh => 'out,err' };

# and job 4 waits for the other 3 jobs
my $job4 = fork { cmd => $command4, depend_on => [ $job1, $job2, $job3 ],
                  child_fh => 'out,err' };

# wait for jobs to finish, then we'll collect output
$_->wait for $job1, $job2, $job3, $job4;
my @output1 = $job1->read_stderr;
my @output2 = $job2->read_stderr;
my @output3 = ($job3->read_stdout, $job3->read_stderr);
my @output4 = ($job4->read_stdout, $job4->read_stderr);
...

答案 2 :(得分:1)

使用Net::OpenSSH::Parallel

# untested!
use Net::OpenSSH::Parallel;
my $pssh = Net::OpenSSH::Parallel->new;

$pssh->add_server(app  => $server{app},  user => 'runuser');
$pssh->add_server(auth => $server{auth}, user => 'runuser');
$pssh->add_server(varn => $server{varn}, user => 'runuser');

$pssh->push('app',  cmd  => @cmd1);
$pssh->push('auth', cmd  => @cmd2);
$pssh->push('auth', cmd  => @cmd3);
$pssh->push('varn', join => '*');
$pssh->push('varn', cmd  => @cmd4);

$pssh->run;

如果您需要传递密码,自动sudo稍微复杂一些,但仍然可以完成。它在模块文档中进行了解释。