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

时间:2009-08-20 11:42:05

标签: perl unix ssh

我有一个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 sleeps然后以随机退出代码退出 除非($ pid){
    打印“连接服务器”;     exec“ssh-copy-id -i /root/.ssh/id_rsa.pub $ uname \ @ $ ip 2>& 1 1> / dev / null”;

    exit int rand 255;
}

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

#display it<br />
print $frame;


    #put在帧列表的末尾
    推@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 #This是throbber。输出看起来并不完全如此,但我无法打印动态变化的输出,我可以 密码:o0O0oXXXXXo0O0o#你说对了,笨蛋也不必在密码提示下来了

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

任何想法,任何人?

3 个答案:

答案 0 :(得分:3)

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

#!/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";

答案 1 :(得分: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 $@;

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

答案 2 :(得分:1)

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

另一方面,我认为你的ssh-copy-id需要很长时间才能完成的原因是因为DNS设置不合适(检查服务器端的sshd日志是否有线索)。 ssh服务器可能会尝试解析客户端IP的反向映射并超时。修复这可能会加速很多事情。

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