我正在从jsch执行cmd,一旦命令完成,我需要显示命令输出。在读取命令输出时,即使在执行命令之后,它也会花费很长时间并且它不会来自while循环。以下是我的代码:
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
JSch jsch = new JSch();
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");
channel=session.openChannel("shell");
OutputStream ops = channel.getOutputStream();
PrintStream ps = new PrintStream(ops, true);
channel.connect();
ps.println(cmd);
InputStream in=channel.getInputStream();
byte[] tmp=new byte[1024];
while(channel.getExitStatus() == -1){
while(in.available()>0){
int i=in.read(tmp, 0, 1024);
if(i<0)break;
System.out.print(new String(tmp, 0, i));
}
if(channel.isClosed()){
System.out.println("exit-status: "+channel.getExitStatus());
break;
}
try{Thread.sleep(3000);}catch(Exception ee){}
}
从我到目前为止观察到的情况来看,只有在SSH守护程序向您发送输出流的所有内容(stdout和stderr)后,channel.isClosed()
才会成立。
如果此内容很大,它将无法完全适合sshd与您的进程之间的TCP连接缓冲区,并且该通道将永远保持打开状态。当你在stdout和stderr中都有很多内容时问题更加严重,因为你不知道优先使用哪个流。对read()
的调用是阻塞而没有超时的可能性。
我们使用的解决方案是在主线程中同时小心地消耗stdout和stderr的所有内容,如果我们不确定它是否会阻塞,则不要在任何流上调用read()
。
StringBuilder result = new StringBuilder();
StringBuilder error = new StringBuilder();
InputStream out = channel.getInputStream();
InputStream err = channel.getErrStream();
while(!channel.isClosed() || out.available() > 0 || err.available() > 0) {
if (out.available() > 0) {
size = out.read(cbuf);
if (size > 0) {
result.append(new String(cbuf, 0, size));
}
// normally the 'if' is useless as available() guarantee size > 0
// it's just an extra-precaution
} else if (err.available() > 0) {
size = err.read(cbuf);
if (size > 0) {
error.append(new String(cbuf, 0, size));
}
} else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
我在命令末尾添加了exit命令。