为什么这个Java进程在作为子进程运行bash之后会挂起?

为什么这个Java进程在作为子进程运行bash之后会挂起?,java,bash,process,Java,Bash,Process,如果我从bash编译并运行以下Java程序: public class BashSuspend { public static void main(String[] args) throws Exception { Process process = new ProcessBuilder("bash -i -c ls".split(" ")) .redirectInput(ProcessBuilder.Redirect.INHERIT)

如果我从bash编译并运行以下Java程序:

public class BashSuspend {

    public static void main(String[] args) throws Exception {
        Process process = new ProcessBuilder("bash -i -c ls".split(" "))
                .redirectInput(ProcessBuilder.Redirect.INHERIT)
                .redirectOutput(ProcessBuilder.Redirect.INHERIT)
                .redirectError(ProcessBuilder.Redirect.INHERIT).start();
        process.waitFor();
        System.in.read();
    }
}
我看到一些奇怪的行为:

matt@Overmind:~/bash-suspend$ java BashSuspend 
BashSuspend.class  BashSuspend.java

[1]+  Stopped                 java BashSuspend
matt@Overmind:~/bash-suspend$
为什么Java进程在作为子进程运行bash之后会挂起


(我正在Ubuntu 15.10上运行openjdk版本“1.8.0_66-internal”。

您的程序正在等待输入,这只有在前台运行时才能执行


我不会等待您忽略的输入,而是将其删除或使用另一种方法。

由于使用了以下内容,您的
[1]+将被停止:

bash -i
它正在交互模式下运行
bash
。删除
-i
选项以修复此问题:

new ProcessBuilder("bash -c ls".split(" "))
根据
曼巴什

-i        If the -i option is present, the shell is interactive.
您还需要注释掉
System.in.read()以避免程序继续运行(它只是在等待输入)

仅需在您停止
错误后向我的答案添加更多信息,您可以运行:

jobs -l
要列出后台运行的作业,它将显示如下内容:

[1]+ 71598 Stopped (tty input): 21 java BashSuspend

这是由于您使用了
System.in.read()
正在交互shell中等待输入,该shell没有附加
tty
,因此它被停止。

程序应始终在前台运行。它可能在等待子进程完成或从stdin读取时阻塞,但这与挂起不同。我感兴趣的是为什么运行“bash-I”会导致java进程挂起。因为
ProcessBuilder
并不是真正运行交互式
bash
。交互式
bash
是指您通常使用的
bash
登录shell,它附带了
tty
,而这里我们没有任何
tty
附件。我不这么认为:如果您只运行
bash-i
,那么bash将正常启动,并附加了一个tty。在任何情况下,这都不能解释为什么父java进程被挂起。从命令行运行
bash-i
与从java代码运行
bash-i
不同,因为在shell中已经附加了
tty
。不,如果从java作为子进程运行
bash-i
,则仍然附加了tty。