分离使用 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
我正在使用 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