分离使用 PTY 和 JSch Java 执行的命令 (sudo) 的标准输出和错误输出

Separating standard and error output of a command (sudo) executed with PTY with JSch Java

我正在使用 Jsch,我的任务是登录服务器和 运行 命令如下

sudo "command"

使用以下代码我能够成功连接并执行命令(没有错误 "sudo: sorry, you must have a tty"

public String runSudo(RemoteHost remoteHost, String[] commands, OperationData operationData, String known_hosts, String id_rsa) {
    String result = "";
    Properties config = new Properties();
    config.put("StrictHostKeyChecking", "no");
    JSch jsch = new JSch();
    // Create a JSch session to connect to the server
    Session session = null;

    try {
        session = jsch.getSession(remoteHost.getUsername(), remoteHost.getHost(), remoteHost.getPort());

        if (remoteHost.getPassword() != null)
            session.setPassword(remoteHost.getPassword());
        else{
            session.setConfig("PreferredAuthentications", "publickey");
            jsch.setKnownHosts(known_hosts);
            jsch.addIdentity(id_rsa);
        }
        session.setConfig(config);
        // Establish the connection
        session.connect();
        logger.debug("Connected...");

        for (int k = 0; k < commands.length; k++) {
            ChannelExec channel = (ChannelExec) session.openChannel("exec");
            channel.setCommand(commands[k]);
            OutputStream outputStreamStdErr = new ByteArrayOutputStream();
            channel.setErrStream(outputStreamStdErr, true);
            channel.setPty(true);

            InputStream in = channel.getInputStream();
            channel.connect();
            byte[] tmp = new byte[1024];
            while (true) {
                while (in.available() > 0) {
                    int i = in.read(tmp, 0, 1024);
                    if (i < 0) {
                        break;
                    }
                    result = result + new String(tmp, 0, i);
                }
                if (channel.isClosed()) {
                    this.exitStatus += channel.getExitStatus();
                    break;
                }
                Thread.sleep(1000);
            }
            String errors = readInputStream(outputstream2inputstream(outputStreamStdErr));
            if (!errors.equals("")) {
                operationData.setErrorMessage(errors);
            }
            channel.getErrStream().close();
            channel.disconnect();
        }
        session.disconnect();
    } catch (JSchException e) {
        operationData.setErrorMessage(e.getMessage());
        logger.error(e.getMessage());
        return result;
    } catch (IOException e) {
        operationData.setErrorMessage(e.getMessage());
        logger.error(e.getMessage());
        return result;
    } catch (InterruptedException e) {
        operationData.setErrorMessage(e.getMessage());
        logger.error(e.getMessage());
        return result;
    } finally {
        return result;
    }
}

但是使用 属性 channel.setPty(true); 我无法读取错误流,因为它是 null 并且错误显示在命令的输出中。 相反,如果我不使用 属性 channel.setPty(true); 我可以读取错误流,但是方法 return 错误 "sudo: sorry, you must have a tty" 我想避免。

有人可以帮助我在同一代码中使用错误流和 属性 channel.setPty(true); 吗?

这是标准的 SSH 行为(至少在 Linux 上),当启用终端仿真时,所有输出都进入 SSH 连接中的一个流。这与您的 Java/JSch 代码无关。

不要将 PTY 用于命令自动化。 PTY 旨在实现供人类使用的交互式终端。

您可以配置 sudo 不需要 PTY。

还有其他选择。
Allowing automatic command execution as root on Linux using SSH.


另一个解决方法是将 stdout 和 stderr 重定向到服务器上的临时文件。然后将文件分别打印到terminal/output.

一个简单的实现可以是这样的:

sudo ... > /tmp/my_stdout 2> /tmp/my_stderr ; echo "stdout:" ; cat /tmp/my_stdout ; echo "stderr:" ; cat /tmp/my_stderr