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