PHP SSH exec()执行失败:与导出变量大小相关
解决SSH传递大数据时exec返回NULL的问题
我之前处理过类似的PHP通过SSH传递大数据的场景,你的情况大概率是碰到了shell命令行参数的长度限制——你用的<<<'$data' here-string方式,本质是把$data的内容塞进了shell的命令行缓冲区,而大多数系统的shell对命令行参数总长度(含环境变量)有严格限制(比如Linux默认是128KB左右,刚好和你说的130KB临界值对应),超过这个阈值后shell会直接报错终止,导致exec返回空的响应数组。
下面给你几个可行的解决方案:
方案1:改用管道通过标准输入传递数据(推荐)
不要把数据塞进命令行,而是用proc_open替代exec,通过管道直接把数据传给远程命令的标准输入,完全绕开命令行长度限制,还能捕获错误输出方便排查问题:
$descriptorspec = [ 0 => ["pipe", "r"], // 标准输入:我们向远程命令写入数据 1 => ["pipe", "w"], // 标准输出:读取远程命令的响应 2 => ["pipe", "w"] // 标准错误:捕获执行过程中的错误信息 ]; // 启动SSH进程 $process = proc_open("ssh -l user subdomain.domain.com bin/prog", $descriptorspec, $pipes); if (is_resource($process)) { // 将大数据写入远程命令的标准输入 fwrite($pipes[0], $data); fclose($pipes[0]); // 关闭输入管道,告诉远程命令数据已传完 // 读取响应和错误信息 $responseContent = stream_get_contents($pipes[1]); $errorContent = stream_get_contents($pipes[2]); fclose($pipes[1]); fclose($pipes[2]); // 获取进程退出状态 $status = proc_close($process); // 处理结果:如果需要和原exec的$response数组格式对齐,可以拆分换行 $response = $status === 0 ? explode("\n", trim($responseContent)) : []; // 有错误时可以打印排查 if ($status !== 0) { echo "执行错误:" . $errorContent; } }
方案2:先上传数据文件再执行命令
如果数据量特别大(比如几百MB以上),可以先把数据写入本地临时文件,用scp传到远程服务器,再让远程命令读取文件内容,最后清理临时文件:
// 创建本地临时文件并写入数据 $localTemp = tempnam(sys_get_temp_dir(), 'ssh_data'); file_put_contents($localTemp, $data); // 上传到远程服务器临时目录 $scpCmd = "scp {$localTemp} user@subdomain.domain.com:/tmp/remote_data.tmp"; exec($scpCmd, $scpResp, $scpStatus); if ($scpStatus === 0) { // 远程执行命令读取文件,执行完删除远程临时文件 $sshCmd = "ssh -l user subdomain.domain.com 'bin/prog < /tmp/remote_data.tmp && rm /tmp/remote_data.tmp'"; exec($sshCmd, $response, $status); } else { echo "文件上传失败:" . implode("\n", $scpResp); } // 清理本地临时文件 unlink($localTemp);
可选:验证系统命令行长度限制
你可以在本地或远程服务器执行以下命令,确认系统的最大命令行参数长度:
getconf ARG_MAX
如果返回值接近131072(128KB),就完全验证了我们的判断。不过修改这个系统配置风险较高,还是用前面两种解决方案更稳妥。
内容的提问来源于stack exchange,提问作者Jeesubmunu
相关产品推荐
相关产品推荐

