Log stdout and stderr from ssh command in the same order it was created

徘徊边缘 提交于 2019-12-29 07:22:07

问题


Short Version: Is it possible to log the stdout and stderr on the local side of a command executed remotely via ssh in the same order as it was output on the remote host? If so, how?

Long Version:

I am attempting to log the standard and error output from a remotely exec'd SSH command (using Jsch) in the same order as it was output by the remote command. In other words, if the remote command writes "a" to stdout, then "b" to stderr, then "c" to stdout, I want the log on the client (local) side to read:

a
b
c

Below is what I have so far. It comes relatively close to what I want, but I think it will be apparent that it does not guarantee the correct output order on the client side.

public int exec(String strCommand) throws ExceptionUnableToExecCommand {
    JSch jsch = new JSch();
    Session session = null;
    ChannelExec channel = null;
    try {
          session = jsch.getSession(user, host, 22);
          UserInfo ui = new cyclOps.jsch.UserInfo(password);
          session.setUserInfo(ui);
          session.connect();
          channel = (ChannelExec) session.openChannel("exec");
          channel.setCommand(strCommand);
          channel.setInputStream(null);
          InputStream in = channel.getInputStream();
          InputStream err = channel.getErrStream();
          channel.connect();
          /* getOutput() defined below. */
          return this.getOutput(channel, in, err);
    } catch (JSchException | IOException e) {
        throw new ExceptionUnableToExecCommand("Unable to execute " + strCommand + " " + this.toString(), e);
    } finally {
        if (channel != null) channel.disconnect();
        if (session != null) session.disconnect();
    }
}

private int getOutput(ChannelExec channel, InputStream in, InputStream err) throws IOException { 
    byte[] tmp = new byte[1024];
    while(true){
        while(in.available() > 0){
            int i=in.read(tmp, 0, 1024);
            if(i<0)break;
            this.sshLogger.logOutputFromSSH(new String(tmp, 0, i));
        }
        while(err.available() > 0){
            int i=err.read(tmp, 0, 1024);
            if(i<0)break;
            this.sshLogger.logOutputFromSSH(new String(tmp, 0, i));
        }
        if(channel.isClosed()){
            return channel.getExitStatus();
        }
        try{Thread.sleep(1000);}catch(Exception ee){}
    }
}

I think I should point out that this is a modified version of Exec.java from the Jsch web site examples.


回答1:


How about the following chunk of code?

channel.setCommand(command);

PipedOutputStream pos=new PipedOutputStream();
PipedInputStream pis=new PipedInputStream(pos);
channel.setOutputStream(pos);
channel.setExtOutputStream(pos);
InputStream in=pis;

channel.connect();



回答2:


You are working with separate streams, so no, there is no way you can possibly guarantee to get the exact same output.

that said, your code has a number of potential problems, among them:

  • InputStream.available is a generally useless method and should be avoided
  • you are reading bytes and converting arbitrary byte arrays to chars/Strings, which could result in broken output (if you are getting multi-byte chars you could end up splitting them).
  • reading 2 different blocking streams with a single thread can result in deadlock


来源:https://stackoverflow.com/questions/21736048/log-stdout-and-stderr-from-ssh-command-in-the-same-order-it-was-created

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