我有一个控制台Java程序,它在一个单独的进程中执行sh -i
,并在进程的输入/输出流和相应的系统流之间复制数据:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
class StreamCopier implements Runnable {
private InputStream in;
private OutputStream out;
public StreamCopier(InputStream in, OutputStream out) {
this.in = in;
this.out = out;
}
public void run() {
try {
int n = 0;
byte[] buffer = new byte[4096];
while (-1 != (n = in.read(buffer))) {
out.write(buffer, 0, n);
out.flush();
}
} catch (IOException e) {
System.out.println(e);
}
}
}
public class Test {
public static void main(String[] args)
throws IOException, InterruptedException {
Process process = Runtime.getRuntime().exec("sh -i");
new Thread(new StreamCopier(
process.getInputStream(), System.out)).start();
new Thread(new StreamCopier(
process.getErrorStream(), System.err)).start();
new Thread(new StreamCopier(
System.in, process.getOutputStream())).start();
process.waitFor();
}
}
在Linux下运行会产生以下结果:
$
[1]+ Stopped java -cp . Test
有人可以澄清为什么应用程序停止了以及如何避免它?
这与我的question on copying streams有关,但我认为这个特殊问题需要单独关注。
答案 0 :(得分:1)
您正被SIGTTIN或SIGTTOU拦截。当这些信号尝试对TTY执行IO时,会将这些信号发送到后台进程。在这种情况下,“背景”表示“不是终端的控制过程组”。我怀疑你要分离的子弹是创建一个新的pgrp并接管你的tty。然后父程序(java)执行IO(在您的情况下可能从TTY读取)并获取SIGTTIN。
确认这一理论的一个简单方法是用更简单的东西(不是贝壳)代替sh,而不是试图接管tty。
答案 1 :(得分:1)
您可以通过调用sh -i +m
之类的shell来关闭作业控制,这将阻止它接管tty。这意味着fg
和bg
命令将不起作用,Ctrl + Z将挂起您的Java应用程序,shell和所有程序都从它开始。
如果你仍然想要作业控制,你应该使用一个伪终端与shell进行通信,这会为shell创建一个新的tty,但我认为Java不支持。