
当开发者尝试通过php脚本(无论是使用原生的ssh2_shell函数还是更高级的phpseclib库)向远程ssh服务器发送较长的命令时,经常会发现命令在远程服务器上执行时被意外地截断或修改。具体表现为在命令字符串中出现[1d]这样的字符序列,例如ont-lineprof [1dile-id而不是正确的ont-lineprofile-id。这种现象通常发生在命令长度达到或超过终端默认的80个字符列宽之后,即使已经尝试通过setwindowcolumns等方法调整终端列宽也无济于事。然而,通过putty等交互式终端客户端手动输入相同的命令则不会出现此问题。
以下是使用SSH2扩展和phpseclib库发送长命令时出现问题的示例代码和输出:
SSH2 扩展示例代码:
$stream = ssh2_shell($session, "vt100", null, 200, 25, SSH2_TERM_UNIT_CHARS); stream_set_blocking($stream, true); usleep(500000); fwrite($stream, "enable\n"); usleep(500000); fwrite($stream, "mmi-mode enable\n"); usleep(500000); fwrite($stream, "aaaa aaaa aaaa aaaa "); // 分段写入长命令 usleep(500000); fwrite($stream, "aaaa aaaa aaaa aaaa "); usleep(500000); fwrite($stream, "aaaa aaaa aaaa aaaa "); usleep(500000); fwrite($stream, "aaaa aaaa aaaa aaaa "); usleep(500000); fwrite($stream, "aaaa aaaa aaaa aaaa \n"); // 即使分段写入也无效 usleep(500000); echo nl2br(fread($stream, 8192)); fclose($stream);
Phpseclib 示例代码:
$ssh = new \phpseclib3\Net\SSH2($ip, 22, 1);
if (!$ssh->login($login, $password)) {
throw new \Exception('Login failed');
}
$ssh->setTerminal("VT100");
$ssh->setWindowColumns(200);
$ssh->write("enable\n");
$ssh->write("mmi-mode enable\n");
$ssh->write("aaaa aaaa aaaa aaaa "); // 分段写入长命令
$ssh->write("aaaa aaaa aaaa aaaa ");
$ssh->write("aaaa aaaa aaaa aaaa ");
$ssh->write("aaaa aaaa aaaa aaaa ");
$ssh->write("aaaa aaaa aaaa aaaa \n");
echo nl2br($ssh->read()); // 一次性读取所有响应
echo $ssh->getLog();
$ssh->disconnect();在这两种情况下,远程服务器的响应中都出现了[1D]字符,导致命令执行失败或参数错误。[1D]的十六进制表示为1b5b3144,它是一个ANSI转义序列,代表“光标后退1字符”(ESC[1D)。
立即学习“PHP免费学习笔记(深入)”;
这个问题的核心不在于命令的长度本身,也不在于是否将长命令分段发送,而在于SSH客户端(PHP脚本)与远程SSH服务器之间的通信缺乏必要的同步机制。
本质上,[1D]的出现是远程终端试图在回显过程中进行光标控制的副作用,而这种副作用在客户端未及时读取并处理响应时被捕获为原始数据。
解决此问题的关键是确保每次发送命令后,PHP脚本都能够等待并读取远程shell的响应,直到识别出预期的命令提示符。这表明远程shell已经处理完前一个命令,并准备好接收下一个命令。
改进后的 Phpseclib 示例代码:
<?php
use phpseclib3\Net\SSH2;
// 假设 $ip, $login, $password 已经定义
$ip = 'your_ssh_host';
$login = 'your_username';
$password = 'your_password';
$ssh = new SSH2($ip, 22); // 默认端口22
if (!$ssh->login($login, $password)) {
throw new \Exception('Login failed');
}
// 设置终端类型和列宽,这仍是良好实践
$ssh->setTerminal("VT100");
$ssh->setWindowColumns(200);
// 1. 等待初始提示符
// 远程服务器的初始提示符可能因设备类型和配置而异
// 例如:MA5683T>
echo "等待初始提示符...\n";
$initialPrompt = 'MA5683T>'; // 根据实际情况修改
$output = $ssh->read($initialPrompt);
echo "收到初始提示符: " . nl2br($output) . "\n";
// 2. 发送 'enable' 命令并等待新的提示符
echo "发送 enable 命令...\n";
$ssh->write("enable\n");
$enablePrompt = 'MA5683T#'; // 'enable' 命令后可能变为特权模式提示符
$output = $ssh->read($enablePrompt);
echo "收到 enable 命令响应: " . nl2br($output) . "\n";
// 3. 发送 'mmi-mode enable' 命令并等待提示符
echo "发送 mmi-mode enable 命令...\n";
$ssh->write("mmi-mode enable\n");
// 假设 'mmi-mode enable' 后提示符不变
$output = $ssh->read($enablePrompt);
echo "收到 mmi-mode enable 命令响应: " . nl2br($output) . "\n";
// 4. 发送长命令并等待提示符
// 现在可以一次性发送整个长命令,因为同步机制已建立
$longCommand = "aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa \n";
echo "发送长命令...\n";
$ssh->write($longCommand);
$output = $ssh->read($enablePrompt); // 等待长命令执行后的提示符
echo "收到长命令响应: " . nl2br($output) . "\n";
echo "完整的SSH通信日志:\n";
echo $ssh->getLog(); // 打印日志以供调试
$ssh->disconnect();
?>关键改进点:
当使用PHP进行SSH自动化,特别是涉及交互式shell和长命令时,理解并正确处理客户端与远程服务器之间的通信同步至关重要。[1D]乱码的出现,正是由于客户端在未确认远程shell准备就绪的情况下,过早地发送了后续数据。通过在每次write()操作后紧跟一个等待并读取预期提示符的read()操作,可以有效地同步通信流,确保命令的正确执行和会话的稳定性。这种同步读写模式是构建健壮SSH自动化脚本的基础。
以上就是解决PHP SSH长命令乱码:同步读写是关键的详细内容,更多请关注php中文网其它相关文章!
PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号