首页 文章

如何从Perl中显示命令“ssh-copy-id”的执行状态?

提问于
浏览
1

我有一个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的建议尝试了代码
死"could not fork: $!"除非定义(我的$ pid = fork);

#child sleeps然后以随机退出代码退出
除非($ pid){
print "Connecting to server "; exec "ssh-copy-id -i /root/.ssh/id_rsa.pub $uname@$ip 2>&1 1>/dev/null";
exit int rand 255;
}

#parent等待孩子完成
$ | = 1;
print "waiting: ";
我的@throbber = qw / . o O o . /;
直到($ pid = waitpid(-1,WNOHANG)){
#get下一帧
我的$ frame = shift @throbber;

#display it
print $frame;

#put在帧列表的末尾
推@throbber,$ frame;

#wait a quarter second
select undef, undef, undef, .25;
#backspace over the frame
print "\b";

}

问题是这样的:
现在,ssh-copy-id在连接到远程服务器时要求输入密码 . 因此,"throbber"输出(即显示的不同大小的圆圈)出现在密码输入之后,看起来很奇怪 . 这是它的样子:
当前输出
连接到远程服务器o0O0o #This是throbber . 输出没有打印动态改变输出,我可以
密码:o0O0oXXXXXo0O0o#你说对了,笨蛋也不必在密码提示下来了

我想要的输出:
连接到远程服务器o0O0o #Throbber应该只在这里显示,无论如何
密码:XXXXX

任何想法,任何人?

3 回答

  • 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()命令之前使用打印,并使用系统的返回值来打印完成消息(正如其他一些答案所建议的那样)?

相关问题