如何在Perl脚本中传递密码实现SSH免交互远程命令执行
失败核心原因
直接替换expect命令后执行失败,是三个逻辑不匹配导致的:
- 原expect命令末尾的
interact指令作用是将程序控制权交还给交互式终端,仅在手动在命令行敲命令时可用。Perl的system调用启动的是无控制终端的子进程,执行到interact时会因为找不到可交互的终端直接崩溃。 - 如果把Perl变量直接写在expect的单引号参数内,Perl不会解析单引号包裹的变量,最终传给ssh的是字面量
$lot、$address,不是从请求里取到的实际参数值。 - 原ssh命令末尾的后台符
&和expect的流监听逻辑冲突,expect需要捕获ssh进程的标准输入输出才能匹配密码提示,直接把进程丢后台会导致expect拿不到输出流,匹配不到密码提示就会超时退出。
正确集成方案
方案1:调整expect命令参数(改动最小,无需安装额外Perl模块)
核心调整点:
- 把
interact替换为expect eof,让expect等远端命令执行完成后自动退出,不需要绑定交互终端 - 对所有传入shell的参数做转义,避免参数带特殊字符(比如邮箱的@、参数里的空格或;、&等符号)导致命令解析错误
- 删掉测试expect时加的多余参数
"ls -lh file" - 调整后台运行符的位置,放在expect命令最外层,保证expect可以正常捕获ssh的输入输出流
替换后的Perl代码片段:
# 引入shell转义模块,避免参数注入和解析错误 use String::ShellQuote; # 对所有可变参数做转义 my $safe_lot = shell_quote($lot); my $safe_pid = shell_quote($$); my $safe_addr = shell_quote($address); my $safe_pass = shell_quote("你的实际SSH登录密码"); system(qq{expect -c ' spawn ssh SERVERNAME /test/abc.csh $safe_lot $safe_pid $safe_addr expect "Password:" send "$safe_pass\r" expect eof ' &});
如果暂时没法安装String::ShellQuote模块,临时使用可以手动给参数里的特殊字符加反斜杠转义,但生产环境强烈建议使用正规转义模块,避免出现异常命令执行问题。
方案2:使用Perl原生Expect模块(稳定性更高)
如果环境允许安装Perl模块,推荐直接使用Expect模块实现逻辑,不需要拼接shell命令,可维护性和稳定性更好:
# 先安装模块:执行 cpanm Expect 完成安装 use Expect; # 启动ssh进程 my $exp = Expect->new; $exp->spawn("ssh", "SERVERNAME", "/test/abc.csh", $lot, $$, $address) or die "启动ssh进程失败: $!"; # 等待密码提示,30秒超时 $exp->expect(30, [ qr/Password:/ => sub { my $handle = shift; $handle->send("你的实际SSH登录密码\r"); exp_continue; } ]); $exp->soft_close();
长期维护建议直接配置SSH密钥免密登录,把执行脚本的本机公钥加到远端服务器的
authorized_keys列表中,完全不需要用expect传递密码,既没有密码硬编码在脚本里的泄露风险,也不会出现交互匹配的异常问题。
内容的提问来源于stack exchange,提问作者prad001
相关产品推荐
相关产品推荐

