【问题标题】:How can I display the execution status of the command "ssh-copy-id" from Perl?如何从 Perl 显示命令“ssh-copy-id”的执行状态?
【发布时间】:2017-04-21 23:29:48
【问题描述】:

我有一个执行此操作的 Perl 脚本:它在我的系统上生成一个 ssh 身份验证密钥,然后将此密钥复制到远程 Linux 系统以进行无密码 ssh 连接。代码如下:

# Generate an rsa key and store it in the given file
system("ssh-keygen -t rsa -N '' -f /root/.ssh/id_rsa 1>/dev/null"); 

# Copy the generated key to a remote system whose username 
# is stored in variable $uname and IP address is stored in variable $ip 
system("ssh-copy-id -i /root/.ssh/id_rsa.pub $uname\@$ip 2>&1 1>/dev/null");

我遇到的问题是:ssh-copy-id 命令需要相当长的时间才能将密钥复制到远程系统。所以,当这个 Perl 脚本运行时,它看起来就像脚本挂起一样。

因此,我想显示“进度消息”:当复制正在进行时,我想显示“SSH 身份验证密钥复制正在进行中”,如果复制失败,则显示“复制失败”,如果它已成功,显示“复制成功”。

我该怎么做呢?

还有一个(基于 Chas 的回答):
我按照 Chas 的建议尝试了代码
死“不能分叉:$!”除非定义(我的 $pid = fork);

#child 休眠然后以随机退出代码退出
除非 ($pid) {
打印“连接到服务器”; exec "ssh-copy-id -i /root/.ssh/id_rsa.pub $uname\@$ip 2>&1 1>/dev/null";
退出 int rand 255;
}

#parent 等待孩子完成
$| = 1;
打印“等待:”;
我的@throbber = qw/。哦哦哦。 /;
直到 ($pid = waitpid(-1, WNOHANG)) {
#获取下一帧
我的 $frame = shift @throbber;

#display it<br />
print $frame;


#把它放在帧列表的末尾
推@throbber, $frame;

#wait a quarter second<br />
select undef, undef, undef, .25;<br />

#backspace over the frame<br />
print "\b";<br />

}

问题是这样的:
现在 ssh-copy-id 在连接到远程服务器时要求输入密码。因此,“throbber”输出(即显示的不同大小的圆圈)出现在看起来很奇怪的密码输入之后。这是它的样子:
电流输出
连接到远程服务器 o0O0o #这是 throbber。输出看起来并不完全像这样,但我无法打印动态变化的输出,可以吗
密码:o0O0oXXXXXo0O0o #你没看错,跳动者也不必要地出现在密码提示符下

我想要的输出:
连接到远程服务器 o0O0o #悸动者应该只显示在这里,其他地方都没有
密码:XXXXX

有什么想法吗?

【问题讨论】:

  • 将 ssh 密钥复制到某个远程系统可能需要多长时间?甚至通过拨号线路?
  • @Manni 您显然从未遇到过 dns-reverse-lookup 和 GSSAPIAuthentication 问题。我不得不坐下来等待一段时间才能连接。
  • 我的 .ssh/config 文件顶部附近的一行显示“GSSAPIAuthentication no”是有原因的。

标签: perl unix ssh


【解决方案1】:

在主进程让用户知道事情并没有停止发生的同时,派生另一个进程为您工作是相当简单的:

#!/usr/bin/perl

use strict;
use warnings;

use POSIX ":sys_wait_h";

die "could not fork: $!" unless defined(my $pid = fork);

#child sleeps then exits with a random exit code
unless ($pid) {
    #your code replaces this code
    #in this case, it should probably just be
    #exec "ssh-copy-id -i /root/.ssh/id_rsa.pub $uname\@$ip 2>&1 1>/dev/null";
    #as that will replace the child process with ssh-copy-id
    sleep 5;
    exit int rand 255;
}

#parent waits for child to finish
$| = 1;
print "waiting: ";
my @throbber = qw/ . o O o . /;
until ($pid = waitpid(-1, WNOHANG)) {
    #get the next frame
    my $frame = shift @throbber;

    #display it
    print $frame;

    #put it at the end of the list of frames
    push @throbber, $frame;

    #wait a quarter second
    select undef, undef, undef, .25;

    #backspace over the frame
    print "\b";
}
#exit code is in bits 8 - 15 of $?, so shift them down to 0 - 7
my $exit_code = $? >> 8;
print "got exit code of $exit_code\n";

【讨论】:

    【解决方案2】:

    system() 返回由等待调用返回的程序的退出状态。如果系统调用成功,则不返回任何内容。

    因此你可以看到这样的代码:

    system( 'ls' ) and die "Unable to call ls: $?";
    

    这很不直观;-)

    我通常会做以下事情:

    my $status = system( 'ls' );
    die "Unable to call ls: $?" if $status;
    

    但是,如果您查看 perldoc,您会发现一个更好的选择:

    system( 'ls' ) == 0
        or die "Unable to call ls: $?"
    

    我会采用这种方法。但我不应该提及Perl Best Practices 书中建议的方法:

    use Perl6::Builtins qw( system );
    
    system 'ls' or die "Unable to run ls: $?";
    

    但是请注意,PBP recommendation list 将您从这一点转向使用autodie

    use autodie qw( system );
    
    eval { system 'ls' };
    die "Unable to run ls: $@" if $@;
    

    所以这可能是未来的规范方式。

    【讨论】:

      【解决方案3】:

      我认为不可能以简单的方式(不借助分叉、计时器等)将进度条添加到通过 system() 运行但不生成输出的单个外部命令。

      另一方面,我认为您的 ssh-copy-id 需要很长时间才能完成的原因是 DNS 设置不正确(检查服务器端的 sshd 日志以获取线索)。 ssh 服务器可能会尝试解析客户端 IP 的反向映射并超时。解决这个问题可能会大大加快速度。

      当涉及到您的消息时。您不能在运行 system() 命令之前使用 print 并使用系统的返回值来打印完成消息(正如其他一些答案所建议的那样)?

      【讨论】:

      • 是的,这会让事情变得不那么复杂:)
      猜你喜欢
      • 1970-01-01
      • 2011-05-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-30
      相关资源
      最近更新 更多