JSch ChannelExec OutputStream 不显示程序输出。但是它适用于 shell 脚本
JSch ChannelExec OutputStream not showing the program output. However it works with shell script
我在本地机器上有一个用 java 编写的程序。该程序使用 JSch 连接到远程计算机,并在用户界面的 MessageConsole
上显示输出。
我可以更新 UI 中的 MessageConsole
,即使我启动的脚本没有终止并输出随机数。我可以阅读 OutputStream
并在 MessageConsole
上显示它。它工作得很好。
我的问题是启动 C 程序后的输出。 OutputStream
上没有显示任何内容。
仅当程序完全终止时才会出现输出。
所有输出要么用
完成
printf
或
std::cout
如果我手动登录远程机器并手动启动程序,我的终端会立即显示所有输出。
我使用 ChannelExec
在远程机器上启动程序:
String program = "sudo ./foo/bar.out";
String continousOutput = "sh ~/foo/bar.sh";
//ProcessWorker(String command, SSHSession ssh, GUI gui)
pw = new ProcessWorker(program, ssh, this);
pw.execute();
ProcessWorker pw
是 SwingWorker
class 以保持连接在后台打开并更新 GUI 而不会锁定它。
这段代码完成了所有脏活。 SSH 连接已经建立并且可以使用。
protected Void doInBackground() throws Exception {
try {
exec = ssh.session.openChannel("exec");
//command is the String program or continousOutput
((ChannelExec)exec).setCommand(command);
exec.setInputStream(null);
((ChannelExec)exec).setErrStream(System.err);
InputStream in = exec.getInputStream();
exec.connect();
byte [] tmp = new byte[1024];
while(true) {
while(in.available()>0) {
int i = in.read(tmp, 0, 1024);
if(i<0)break;
String response = new String(tmp, 0, i);
check(response); //parse response
System.out.println(response);
//this.publish(); //desperate try to get something
}
if(exec.isClosed()) {
if(in.available() > 0) continue;
System.out.println("exit-status: " + exec.getExitStatus());
break;
}
try {Thread.sleep(1000);} catch(Exception ee) {}
}
exec.disconnect();
} catch (JSchException | IOException e) {
e.printStackTrace();
}
return null;
}
我不知道问题出在哪里。我的意思是它与脚本完美配合。
有什么想法吗?
简短的回答是为远程会话分配 PTY(伪 TTY)。这可能会导致远程进程在 运行ning 时发出其输出,而不是在最后:
exec.setPty(true);
当您通过 unix 命令行 运行 程序时,您是通过 TTY 设备与该程序通信。当您通过 SSH 启动程序而没有 为会话请求 PTY (TTY) 时,您是通过一组管道与程序通信,每个管道用于标准输入、输出和错误。
Unix 程序通常会缓冲它们的输出。缓冲行为内置于大多数 C 程序使用的标准 I/O 逻辑中(也被 perl、python 等使用或复制)。写入 TTY 时,典型的行为是缓冲输出数据,直到写入整行(即程序写入换行符),然后将整行立即写入 TTY。这就是您 运行 交互式程序时的体验。
当写入管道或文件时,默认行为是缓冲直到缓冲区被填满。缓冲区的大小可能为 8 KB。此行为基于这样的假设,即输出不会发送给人类,因此程序应尽可能以最有效的方式运行。
你的描述听起来像是程序正在对其输出进行块缓冲,并且它没有产生足够的输出来在程序完成之前刷新缓冲区。您希望程序对其输出进行行缓冲(或完全取消缓冲)。有几种方法可以做到这一点:
- 你说程序在你 运行 交互时工作正常。正如我所指出的,为 ssh 会话请求 PTY 可能会产生这种行为。
- 您可以查看系统上是否有类似
stdbuf
or unbuffer
的程序。这些可用于改变进程的缓冲行为。
- 如果您有相关程序的源代码,您可以在程序执行时的某些点向 alter the default buffering behavior, or to flush the buffer 添加逻辑。
我在本地机器上有一个用 java 编写的程序。该程序使用 JSch 连接到远程计算机,并在用户界面的 MessageConsole
上显示输出。
我可以更新 UI 中的 MessageConsole
,即使我启动的脚本没有终止并输出随机数。我可以阅读 OutputStream
并在 MessageConsole
上显示它。它工作得很好。
我的问题是启动 C 程序后的输出。 OutputStream
上没有显示任何内容。
仅当程序完全终止时才会出现输出。
所有输出要么用
printf
或
std::cout
如果我手动登录远程机器并手动启动程序,我的终端会立即显示所有输出。
我使用 ChannelExec
在远程机器上启动程序:
String program = "sudo ./foo/bar.out";
String continousOutput = "sh ~/foo/bar.sh";
//ProcessWorker(String command, SSHSession ssh, GUI gui)
pw = new ProcessWorker(program, ssh, this);
pw.execute();
ProcessWorker pw
是 SwingWorker
class 以保持连接在后台打开并更新 GUI 而不会锁定它。
这段代码完成了所有脏活。 SSH 连接已经建立并且可以使用。
protected Void doInBackground() throws Exception {
try {
exec = ssh.session.openChannel("exec");
//command is the String program or continousOutput
((ChannelExec)exec).setCommand(command);
exec.setInputStream(null);
((ChannelExec)exec).setErrStream(System.err);
InputStream in = exec.getInputStream();
exec.connect();
byte [] tmp = new byte[1024];
while(true) {
while(in.available()>0) {
int i = in.read(tmp, 0, 1024);
if(i<0)break;
String response = new String(tmp, 0, i);
check(response); //parse response
System.out.println(response);
//this.publish(); //desperate try to get something
}
if(exec.isClosed()) {
if(in.available() > 0) continue;
System.out.println("exit-status: " + exec.getExitStatus());
break;
}
try {Thread.sleep(1000);} catch(Exception ee) {}
}
exec.disconnect();
} catch (JSchException | IOException e) {
e.printStackTrace();
}
return null;
}
我不知道问题出在哪里。我的意思是它与脚本完美配合。
有什么想法吗?
简短的回答是为远程会话分配 PTY(伪 TTY)。这可能会导致远程进程在 运行ning 时发出其输出,而不是在最后:
exec.setPty(true);
当您通过 unix 命令行 运行 程序时,您是通过 TTY 设备与该程序通信。当您通过 SSH 启动程序而没有 为会话请求 PTY (TTY) 时,您是通过一组管道与程序通信,每个管道用于标准输入、输出和错误。
Unix 程序通常会缓冲它们的输出。缓冲行为内置于大多数 C 程序使用的标准 I/O 逻辑中(也被 perl、python 等使用或复制)。写入 TTY 时,典型的行为是缓冲输出数据,直到写入整行(即程序写入换行符),然后将整行立即写入 TTY。这就是您 运行 交互式程序时的体验。
当写入管道或文件时,默认行为是缓冲直到缓冲区被填满。缓冲区的大小可能为 8 KB。此行为基于这样的假设,即输出不会发送给人类,因此程序应尽可能以最有效的方式运行。
你的描述听起来像是程序正在对其输出进行块缓冲,并且它没有产生足够的输出来在程序完成之前刷新缓冲区。您希望程序对其输出进行行缓冲(或完全取消缓冲)。有几种方法可以做到这一点:
- 你说程序在你 运行 交互时工作正常。正如我所指出的,为 ssh 会话请求 PTY 可能会产生这种行为。
- 您可以查看系统上是否有类似
stdbuf
orunbuffer
的程序。这些可用于改变进程的缓冲行为。 - 如果您有相关程序的源代码,您可以在程序执行时的某些点向 alter the default buffering behavior, or to flush the buffer 添加逻辑。