【问题标题】:How to run multiple perl open commands asynchronously and display output in order如何异步运行多个perl open命令并按顺序显示输出
【发布时间】:2013-04-08 05:51:48
【问题描述】:

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

【问题讨论】:

  • 查看stackoverflow.com/questions/1752357/… tsee 的答案。此外,您问题中唯一的异步内容是 pid1 vs pid2/3。其他一切都是顺序的。
  • 您的预期效果是正确的,尽管目前所有 4 个命令都是异步运行的。
  • 在创建 $pid3 之前,请确保您处理并关闭了 $SSH2。然后在创建 $pid4 之前,确保处理并关闭 $SSH1 和 $SSH3。但是有比使用 open() 更好的方法,请参阅上面链接中的建议。

标签: perl asynchronous


【解决方案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 $!;

【讨论】:

    【解决方案2】:

    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);
    ...
    

    【讨论】:

      【解决方案3】:

      使用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 会稍微复杂一些,但仍然可以完成。模块文档中对此进行了解释。

      【讨论】:

      • 就我而言,我不需要传递密码,因为我使用的是 SSH 密钥。
      • @HighwayofLife:我指的不是 ssh 身份验证。通常,当您使用sudo 运行某些命令时,它会要求您输入额外的密码。尽管在您的环境中,sudo 很可能被配置为不要求输入密码。
      • 啊,是的……你是对的。我的 sudo 用户是 wheel 组的一部分,默认情况下不需要密码即可运行 sudo 命令。但对于可能遇到该问题的其他人来说,这是一件好事。
      猜你喜欢
      • 1970-01-01
      • 2014-10-05
      • 1970-01-01
      • 1970-01-01
      • 2019-08-30
      • 1970-01-01
      • 2010-12-17
      • 2017-01-24
      • 1970-01-01
      相关资源
      最近更新 更多