从ssh命令以与创建它的顺序相同的顺序记录stdout和stderr

短版本:是否可以通过ssh以与远程主机上输出的顺序相同的顺序在远程执行的命令的本地端记录stdout和stderr? 如果是这样,怎么样?

长版:

我试图以与远程命令输出的顺序相同的顺序记录远程执行的SSH命令(使用Jsch)的标准和错误输出。 换句话说,如果远程命令将“a”写入stdout,然后将“b”写入stderr,然后将“c”写入stdout,我希望客户端(本地)端的日志读取:

一个
b
C

以下是我到目前为止的情况。 它与我想要的相对接近,但我认为很明显它不能保证客户端的输出顺序正确。

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){ 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){} } } 

我想我应该指出这是来自Jsch网站示例的Exec.java的修改版本。

下面的代码块怎么样?

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

您正在使用单独的流,所以不,您无法保证获得完全相同的输出。

那就是说,你的代码有很多潜在的问题,其中包括:

  • InputStream.available是一种通常无用的方法,应该避免使用
  • 你正在读取字节并将任意字节数组转换为字符串/字符串,这可能导致输出中断(如果你得到多字节字符,你最终可能会将它们拆分)。
  • 使用单个线程读取2个不同的阻塞流可能会导致死锁