How to read JSch command output?

巧了我就是萌 提交于 2019-12-17 09:31:56

问题


I have the following code:

JSch jsch = new JSch();
jsch.setKnownHosts(dotSshDir + "/known_hosts");
jsch.addIdentity(dotSshDir + "/id_rsa");

Session session = jsch.getSession(userName, hostname, 22);
session.connect();

ChannelExec channel = (ChannelExec) session.openChannel("exec");
channel.setCommand(command);
channel.setInputStream(null);
channel.setErrStream(System.err);
Reader reader = new InputStreamReader(channel.getInputStream());

char[] buf = new char[1024];
int numRead;
while ((numRead = reader.read(buf)) != -1) {
    String readData = String.valueOf(buf, 0, numRead);
    result.append(readData);
    buf = new char[1024];
}

It's hanging trying to read from the reader. How do I fix this? How do I go about hunting down what's happening?


回答1:


The hanging was actually due to some unbalanced quotes in the command.

For posterity, the final code (after dealing with some other issues) is:

public String call(String hostname, String[] argv) throws SubprocessException {
    StringBuffer result = new StringBuffer();

    Session session = null;
    ChannelExec channel = null;

    try {
        // TODO: Emit friendly error if ~/.ssh doesn't exist.
        String dotSshDir = System.getProperty("user.home") + "/.ssh";
        String userName = System.getProperty("user.name");

        // TODO: Emit friendly error if ~/.ssh/known_hosts doesn't exist.
        jSch.setKnownHosts(dotSshDir + "/known_hosts");
        // TODO: Emit friendly error if ~/.ssh/id_rsa doesn't exist.
        jSch.addIdentity(dotSshDir + "/id_rsa");

        session = jSch.getSession(userName, hostname, 22);
        session.connect();

        channel = (ChannelExec) session.openChannel("exec");
        channel.setCommand(Joiner.on(" ").join(argv));
        channel.setInputStream(null);
        InputStream stdout = channel.getInputStream();
        InputStream stderr = channel.getErrStream();
        channel.connect();

        waitForChannelClosed(channel);

        if (channel.getExitStatus() != 0) {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(stderr));
            readAll(bufferedReader, result);

            throw new Exception(result.toString());
        } else {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(stdout));
            readAll(bufferedReader, result);
        }
    } catch (Exception e) {
        throw new SubprocessException(e);
    } finally {
        if (channel != null) {
            channel.disconnect();
        }

        if (session != null) {
            session.disconnect();
        }
    }

    return result.toString();
}



回答2:


The accepted answer by @NoelYap is wrong.

One has to read the output continuously, while waiting for the command to finish. Otherwise, if the command produces enough output to fill in an output buffer, the command will hang, waiting for the buffer to be consumed, what never happens. So you get a deadlock.

The following example reads both stdout and stderr continuously, while monitoring a command status. It is based on the official JSch exec.java example (just adds a reading of stderr).

ChannelExec channel = (ChannelExec)session.openChannel("exec");

channel.setCommand(
    "for((i=1;i<=10000;i+=2)); do echo \"Long output - $i\"; done ; " +
    "echo error output >&2");
InputStream commandOutput = channel.getExtInputStream();

StringBuilder outputBuffer = new StringBuilder();
StringBuilder errorBuffer = new StringBuilder();

InputStream in = channel.getInputStream();
InputStream err = channel.getExtInputStream();

channel.connect();

byte[] tmp = new byte[1024];
while (true) {
    while (in.available() > 0) {
        int i = in.read(tmp, 0, 1024);
        if (i < 0) break;
        outputBuffer.append(new String(tmp, 0, i));
    }
    while (err.available() > 0) {
        int i = err.read(tmp, 0, 1024);
        if (i < 0) break;
        errorBuffer.append(new String(tmp, 0, i));
    }
    if (channel.isClosed()) {
        if ((in.available() > 0) || (err.available() > 0)) continue; 
        System.out.println("exit-status: " + channel.getExitStatus());
        break;
    }
    try { 
      Thread.sleep(1000);
    } catch (Exception ee) {
    }
}

System.out.println("output: " + outputBuffer.toString());
System.out.println("error: " + errorBuffer.toString());

channel.disconnect();

If you add while (!channel.isClosed()) {} after the channel.connect();, you will see that with a sufficiently large i in the shell for loop (10000 is enough with in my environment), the loop never finishes.



来源:https://stackoverflow.com/questions/6902386/how-to-read-jsch-command-output

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!