使用Perl的Net::OpenSSH远程执行sudo命令遇错误,请求协助解决
解决Net::OpenSSH执行sudo命令报错及多命令执行问题
问题场景
以普通用户身份通过SSH连接远程主机,需执行以下环境配置及sudo权限命令:
export http_proxy=http://xxxx.xx.xx.com:8080 export https_proxy=http://xxxx-xxxx.xx.xx.com:8080 sudo /etc/init.d/filebeat restart sudo tail -f /var/log/filebeat/filebeat
使用Perl的Net::OpenSSH模块执行Filebeat重启命令时,代码如下:
my $ssh = Net::OpenSSH->new("$username\@$Hostname", password => $pass); my @cmdF = "/etc/init.d/filebeat restart"; print "Restarting filebeat for $webHosts[$i]"; my @cmdF = "/etc/init.d/filebeat restart"; my $outputF = $ssh->capture({stdin_data=>"$pass\n"}, 'sudo', '-Sk', '-p', '', '-', @cmdF); print "\nCurrent Filebeat Status : $outputF\n\n";
执行后触发错误:
sudo: /etc/init.d/filebeat restart: command not found
错误原因及修复方案
1. 修复sudo命令参数传递错误
核心原因:@cmdF将整个命令作为单个元素传递,sudo会把/etc/init.d/filebeat restart当作完整命令名查找,导致找不到命令。需将命令拆分为独立参数:
修改命令定义:
my @cmdF = ('/etc/init.d/filebeat', 'restart');
调整执行代码(移除多余的-参数,该参数用于从标准输入读命令,直接传递参数更可靠):
my $outputF = $ssh->capture({stdin_data => "$pass\n"}, 'sudo', '-Sk', '-p', '', @cmdF);
2. 执行环境变量配置命令
远程主机设置http_proxy和https_proxy为普通用户权限操作,可直接通过capture方法执行:
# 设置http_proxy $ssh->capture('export http_proxy=http://xxxx.xx.xx.com:8080'); # 设置https_proxy $ssh->capture('export https_proxy=http://xxxx-xxxx.xx.xx.com:8080');
若需让后续sudo命令继承这些环境变量,有两种方式:
- 远程主机修改
sudoers配置,添加Defaults env_keep += "http_proxy https_proxy" - 在sudo命令中直接指定环境变量:
my $outputF = $ssh->capture({stdin_data => "$pass\n"}, 'sudo', '-Sk', '-p', '', 'http_proxy=http://xxxx.xx.xx.com:8080', 'https_proxy=http://xxxx-xxxx.xx.xx.com:8080', @cmdF);
3. 处理sudo tail -f实时输出
tail -f是持续输出命令,capture方法会等待命令结束,无法获取实时流。需使用pipe_out方法处理:
my ($fh, $pid) = $ssh->pipe_out({stdin_data => "$pass\n"}, 'sudo', '-Sk', '-p', '', 'tail', '-f', '/var/log/filebeat/filebeat'); # 实时读取日志输出 while (my $line = <$fh>) { print "Filebeat Log: $line"; } # 结束后清理资源 close $fh; waitpid $pid, 0;
完整整合代码
my $ssh = Net::OpenSSH->new("$username\@$Hostname", password => $pass); die "SSH连接失败: " . $ssh->error if $ssh->error; # 配置环境变量 $ssh->capture('export http_proxy=http://xxxx.xx.xx.com:8080'); $ssh->capture('export https_proxy=http://xxxx-xxxx.xx.xx.com:8080'); # 重启Filebeat my @restart_cmd = ('/etc/init.d/filebeat', 'restart'); print "正在为 $webHosts[$i] 重启Filebeat...\n"; my $restart_output = $ssh->capture({stdin_data => "$pass\n"}, 'sudo', '-Sk', '-p', '', @restart_cmd); print "Filebeat当前状态: $restart_output\n\n"; # 实时查看Filebeat日志 my ($log_fh, $log_pid) = $ssh->pipe_out({stdin_data => "$pass\n"}, 'sudo', '-Sk', '-p', '', 'tail', '-f', '/var/log/filebeat/filebeat'); print "开始流式输出Filebeat日志...\n"; while (my $log_line = <$log_fh>) { print $log_line; } close $log_fh; waitpid $log_pid, 0;
内容的提问来源于stack exchange,提问作者Daksha
相关产品推荐
相关产品推荐

