1

我正在尝试对多个服务器异步运行多个 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'\"");
4

3 回答 3

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 $!;
于 2013-04-05T04:51:04.083 回答
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);
...
于 2013-04-05T15:51:24.763 回答
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会稍微复杂一些,但仍然可以完成。它在模块文档中进行了解释。

于 2013-04-07T21:51:48.263 回答