使用JSch执行Linux命令后读取输出陷入无限循环问题排查
解决JSch执行Linux命令后输出读取慢、无法退出循环的问题
问题根源
你当前用的是shell通道,这个通道是模拟交互式终端会话的,不是用来执行单条命令的专用通道。执行完你的cmd后,shell会停在提示符等待下一条命令,不会主动关闭通道,这就导致:
channel.getExitStatus()一直返回-1(表示通道还在运行),外层while循环永远无法退出- 每次循环都sleep3秒,直接导致输出读取的延迟感很强
推荐解决方案:使用exec通道(专门执行单条命令)
exec通道是JSch专门为执行单个命令设计的,命令执行完成后会自动关闭通道,能正确返回退出状态,读取输出也更高效。修改后的代码如下:
java.util.Properties config = new java.util.Properties(); config.put("StrictHostKeyChecking", "no"); JSch jsch = new JSch(); Session session = jsch.getSession(user, host, 22); session.setConfig("PreferredAuthentications", "publickey,keyboard-interactive,password"); session.setPassword(password); session.setConfig(config); session.connect(); System.out.println("Connected"); // 改用exec通道 ChannelExec channel = (ChannelExec) session.openChannel("exec"); // 设置要执行的命令 channel.setCommand(cmd); // 获取输入流(命令输出) InputStream in = channel.getInputStream(); // 获取错误流(避免命令输出到stderr时被阻塞) InputStream err = channel.getErrStream(); channel.connect(); // 读取标准输出 byte[] tmp = new byte[1024]; while (true) { while (in.available() > 0) { int i = in.read(tmp, 0, 1024); if (i < 0) break; System.out.print(new String(tmp, 0, i)); } // 读取错误输出 while (err.available() > 0) { int i = err.read(tmp, 0, 1024); if (i < 0) break; System.err.print(new String(tmp, 0, i)); } // 通道关闭时退出循环 if (channel.isClosed()) { System.out.println("exit-status: " + channel.getExitStatus()); break; } // 短时间睡眠避免空转,减少延迟 try { Thread.sleep(100); } catch (Exception ee) {} } // 关闭资源 channel.disconnect(); session.disconnect();
如果必须使用shell通道的处理方式
如果因为某些场景需要保留shell通道,你需要在命令末尾加上exit,让shell执行完命令后主动退出,这样通道才会关闭:
// 原代码中发送命令的地方修改为 ps.println(cmd + "; exit");
同时可以把3秒的长睡眠改成100ms左右的短睡眠,减少输出延迟。但要注意,shell通道会包含shell的提示符等额外内容,需要自己处理过滤,整体不如exec通道干净高效。
额外注意点
- 一定要处理错误流(
getErrStream),如果命令有错误输出到stderr而你没读取,可能会导致通道阻塞 - 使用完通道和session后记得调用
disconnect()释放资源
内容的提问来源于stack exchange,提问作者ASR
相关产品推荐
相关产品推荐

