【问题标题】:Reading output after Linux cmd execution is going to infinite loop从 Linux 命令执行读取输出将进入无限循环
【发布时间】:2019-01-30 09:46:17
【问题描述】:

我正在从 jsch 执行 cmd ,一旦命令完成,我需要显示命令输出。在读取命令输出时,它需要很长时间,并且即使在执行命令之后,它的 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){}
        }

【问题讨论】:

    标签: java linux jakarta-ee java-io jsch


    【解决方案1】:

    据我目前观察到的,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();
                    }
                }
            }
    

    【讨论】:

      【解决方案2】:

      我在命令末尾添加了 exit 命令。

      【讨论】:

        【解决方案3】:

        当我从 UNIX 服务器读取 InputStream 时,我遇到了同样的问题。

        out.available() 在 while 循环中读取字节后变为 0,同时我们应该注意到 channel.getExitStatus() 为 -1,表示失败。命令执行失败。

        因此,我决定使用来自channel.getOutputStream() 的日志。下面是我创建的一个可重用方法,它有助于在远程服务器中执行命令并将日志作为字符串返回。我们可以操作包含服务器输出的日志数据,(有些事情可能不正确,请忽略)

        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 "";
                }
        
            }
        

        确保关闭频道和流以避免泄漏...如果我犯了任何错误,请随时提供建议。

        我知道这是一种解决方法,但没有任何其他可行的解决方案...

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-09-23
          • 2020-01-17
          • 1970-01-01
          • 1970-01-01
          • 2021-10-13
          • 2012-03-12
          相关资源
          最近更新 更多