How to execute interactive commands and read their

2020-07-24 16:15发布

问题:

Currently I can use the JSch library to execute remote commands in a SSH session like this:

JSch jsch = new JSch();
Session session = jsch.getSession(username, host, 22);
session.setPassword(password);

Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);

session.connect();

ChannelExec channel = (ChannelExec) session.openChannel("exec");
BufferedReader in = new BufferedReader(new InputStreamReader(channel.getInputStream()));

channel.setCommand("ls -l");
channel.connect();

StringBuilder output = new StringBuilder();

String s = null;
while((s = in.readLine()) != null){
    output.append(s + "\n");
}

System.out.println(output);

The commands that returns the full value right away works fine, but some interactive commands (like "Docker run") returns values every second or so (for a couple of minutes), and the previous code only read the first value returned.

Is there any way to read the values as they are returned by the command?


EDIT

This is my code now:

public String executeCommand(String cmd) {
    try {
        Channel channel = session.openChannel("shell");

        OutputStream inputstream_for_the_channel = channel.getOutputStream();
        PrintStream commander = new PrintStream(inputstream_for_the_channel, true);

        channel.setOutputStream(System.out, true);

        channel.connect();

        commander.println(cmd);

        commander.close();

        do {
            Thread.sleep(1000);
        } while(!channel.isEOF());
    } catch(Exception e) {
        e.printStackTrace();
    }
    return null;
}

回答1:

The way the JSch getInputStream is implemented, its read seems to return -1 not only when the session/channel closes, but also when there's momentarily no input data.

Your should better use channel.isClosed() to test if session/channel closed. See examples/Sudo.java:

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));
  }
  if(channel.isClosed()){
    System.out.println("exit-status: "+channel.getExitStatus());
    break;
  }
  try{Thread.sleep(1000);}catch(Exception ee){}
}

Your code with "shell" channel prints the output on console, because you have instructed it so by:

channel.setOutputStream(System.out, true);

You have to implement the output stream to write to a string. See Get an OutputStream into a String

Or actually, you should be able to use the same code as with the "exec" channel.