Linux cmd 执行后读取输出将进入无限循环
Reading output after Linux cmd execution is going to infinite loop
我正在从 jsch 执行命令,命令完成后我需要显示命令输出。在读取命令输出时需要很长时间,即使在执行命令后它的 ns 也不是来自 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){}
}
我在命令末尾添加了exit命令
据我目前观察到的情况,channel.isClosed()
只有在 SSH 守护程序向您发送了输出流(stdout 和 stderr)的所有内容后才为真。
如果这个内容很大,它不会完全适合 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();
}
}
}
当我从 UNIX 服务器读取 InputStream 时,我遇到了同样的问题。
out.available()
在 while 循环中读取字节后变为 0,同时我们应该注意到 channel.getExitStatus()
是 -1,表示失败。命令执行失败。
因此,我决定使用channel.getOutputStream()
中的日志。下面是我创建的一个可重用方法,它有助于在远程服务器中执行命令并将 returns 日志作为字符串。我们可以操作包含服务器输出的日志数据,(有些地方可能不合适,请忽略)
public static String executeCommand(String command, int timeout) {
try {
Channel channel = getSession().openChannel("shell");
setChannel(channel);
PrintStream standard = System.out;
OutputStream out = getChannel().getOutputStream();
PrintStream commander = new PrintStream(out, true);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos);
getChannel().setOutputStream(ps, true);
getChannel().connect();
for (String c : command.split(";")) {
commander.println(c);
try {Thread.sleep(timeout);}catch(Exception e) {}
}
ps.flush();
commander.flush();
System.setOut(standard);
System.out.println(baos.toString());
out.close();
baos.close();
System.out.println("Command executed successfully - "+command);
unloadChannel();
return baos.toString();
}catch (JSchException e) {
System.out.println("Channel connection failed");
e.printStackTrace();
return "";
//throw new RuntimeException("Shell channel connection failed");
}catch (IOException e) {
System.out.println("IO Exception occurred");
e.printStackTrace();
return "";
}
}
确保关闭频道和流以避免泄漏...如果我有任何错误,请随时提供建议。
我知道这是一种解决方法,但没有任何其他可行的解决方案...
我正在从 jsch 执行命令,命令完成后我需要显示命令输出。在读取命令输出时需要很长时间,即使在执行命令后它的 ns 也不是来自 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){}
}
我在命令末尾添加了exit命令
据我目前观察到的情况,channel.isClosed()
只有在 SSH 守护程序向您发送了输出流(stdout 和 stderr)的所有内容后才为真。
如果这个内容很大,它不会完全适合 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();
}
}
}
当我从 UNIX 服务器读取 InputStream 时,我遇到了同样的问题。
out.available()
在 while 循环中读取字节后变为 0,同时我们应该注意到 channel.getExitStatus()
是 -1,表示失败。命令执行失败。
因此,我决定使用channel.getOutputStream()
中的日志。下面是我创建的一个可重用方法,它有助于在远程服务器中执行命令并将 returns 日志作为字符串。我们可以操作包含服务器输出的日志数据,(有些地方可能不合适,请忽略)
public static String executeCommand(String command, int timeout) {
try {
Channel channel = getSession().openChannel("shell");
setChannel(channel);
PrintStream standard = System.out;
OutputStream out = getChannel().getOutputStream();
PrintStream commander = new PrintStream(out, true);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos);
getChannel().setOutputStream(ps, true);
getChannel().connect();
for (String c : command.split(";")) {
commander.println(c);
try {Thread.sleep(timeout);}catch(Exception e) {}
}
ps.flush();
commander.flush();
System.setOut(standard);
System.out.println(baos.toString());
out.close();
baos.close();
System.out.println("Command executed successfully - "+command);
unloadChannel();
return baos.toString();
}catch (JSchException e) {
System.out.println("Channel connection failed");
e.printStackTrace();
return "";
//throw new RuntimeException("Shell channel connection failed");
}catch (IOException e) {
System.out.println("IO Exception occurred");
e.printStackTrace();
return "";
}
}
确保关闭频道和流以避免泄漏...如果我有任何错误,请随时提供建议。
我知道这是一种解决方法,但没有任何其他可行的解决方案...