RedHat Linux下编写PHP x3270自动化脚本实现登录发命令并存屏到文本
问题修复方案与实现示例
核心问题原因
- 你调用的是GUI版本的
x3270,启动后会进入图形交互模式,无法通过标准输入输出完成自动化控制,应该使用同套件的脚本专用版本s3270,无需图形界面,完全基于命令流交互。 - 代码存在语法错误:
process = proc_open(...)缺少变量前缀$,会触发语法报错。 - 非阻塞流的读取逻辑有缺陷,没有超时机制,匹配不到指定文本就会进入死循环,
ascii()命令的调用位置也不符合s3270的交互规则。 - 缺少屏幕内容持久化逻辑,只要把
ascii()返回的内容写入文件即可实现保存需求。
修正后完整代码
首先在RedHat环境执行yum install x3270安装完整套件,确认s3270命令可用后使用以下代码:
<?php // 业务配置 $targetHost = '你的目标主机地址'; $targetPort = '目标端口'; $screenSavePath = './screen_output.txt'; // 屏幕内容保存路径 $descriptorspec = array( 0 => array("pipe", "r"), // 标准输入:给s3270发控制命令 1 => array("pipe", "w"), // 标准输出:读取s3270返回的结果 2 => array("pipe", "w") // 标准错误:排查交互异常 ); // 调用s3270而非x3270,可执行which s3270确认实际路径 $process = proc_open('/usr/bin/s3270', $descriptorspec, $pipes, null, null); if (!is_resource($process)) { die("启动s3270失败"); } // 设置流非阻塞模式 stream_set_blocking($pipes[0], 0); stream_set_blocking($pipes[1], 0); stream_set_blocking($pipes[2], 0); /** * 等待匹配屏幕指定文本,带超时机制避免死循环 * @param string $matchTxt 要匹配的文本 * @param int $timeout 超时时间/秒 * @return false|string 匹配成功返回屏幕内容,超时返回false */ function expecttxt(string $matchTxt, int $timeout = 10) { global $pipes; $startTime = time(); $buffer = ''; while (time() - $startTime < $timeout) { // 读取所有可用输出 while ($line = fgets($pipes[1])) { $buffer .= $line; if (preg_match("/".preg_quote($matchTxt, '/')."/i", $buffer)) { return $buffer; } } // 打印错误日志便于排查 while ($err = fgets($pipes[2])) { error_log("s3270交互错误:".$err); } usleep(500000); // 0.5秒轮询,降低CPU占用 } return false; } /** * 保存屏幕内容到txt文件 * @param string $content 屏幕内容 * @param string $path 保存路径 */ function saveScreen(string $content, string $path) { file_put_contents($path, "===== ".date("Y-m-d H:i:s")." 屏幕快照 =====\n".$content.PHP_EOL, FILE_APPEND); } // 业务流程实现 // 1. 连接目标设备 fwrite($pipes[0], "connect($targetHost:$targetPort)\n"); if (!expecttxt("connected")) { die("连接目标设备超时"); } // 2. 读取初始屏幕、匹配CDN标识并保存 fwrite($pipes[0], "ascii()\n"); $screenContent = expecttxt("CDN"); if (!$screenContent) { die("等待CDN标识超时"); } saveScreen($screenContent, $screenSavePath); // 3. 输入会话名并回车 fwrite($pipes[0], "string(session-name)\n"); fwrite($pipes[0], "enter\n"); // 4. 读取会话状态页面并保存 fwrite($pipes[0], "ascii()\n"); $screenContent = expecttxt("SESSION STATUS"); if (!$screenContent) { die("等待会话状态页面超时"); } saveScreen($screenContent, $screenSavePath); // 5. 按下PF2键 fwrite($pipes[0], "PF(2)\n"); // 6. 读取下一页内容并保存 fwrite($pipes[0], "ascii()\n"); $screenContent = expecttxt("NEXT INFO"); if (!$screenContent) { die("等待下一页信息超时"); } saveScreen($screenContent, $screenSavePath); // 结束流程,释放资源 fwrite($pipes[0], "quit()\n"); proc_close($process); ?>
使用说明
- 替换代码中主机地址、端口、会话名等占位内容为实际业务参数即可运行。
- 每一步的屏幕快照会按执行时间追加到指定的txt文件中,便于后续回溯。
- 所有交互步骤都设置了超时保护,可根据实际网络情况调整
expecttxt的第二个参数修改超时时间。
内容的提问来源于stack exchange,提问作者Luciano Fattore
相关产品推荐
相关产品推荐

