在Java中运行交互式shell程序

时间:2015-04-19 17:06:17

标签: java exec

我正在尝试(并且失败)找出如何在Java中运行完全交互式shell程序。

以下是该方案:

我有一个跨平台的大型GUI应用程序,用Java编写。我试图添加一个交互式命令行环境来运行无头。事情的一面都很好,花花公子。但是,主GUI的一个功能是编辑文件。现在,对于命令行界面,我希望能够执行外部编辑器来编辑文件,然后返回到我保存和退出后的位置。例如,在Linux上它可以执行" vi / path / to / file"。

那么如何以键盘和显示器与应用程序完全交互的方式执行该命令呢?我不希望它在后台运行。我不希望它通过Java重定向IO,我只想让一个命令在前台运行"#34;直到它存在。

就像我在C中使用system()功能一样。

到目前为止,我发现的所有内容都在后台运行命令或通过Java管理IO,这对于交互式(非线路模式)应用程序不起作用。

哦,还有一个最后的警告:我仅限于Java 1.6兼容性,因此我无法使用ProcessBuilder做一些奇特的事情。

这是强制性的SSCCE:

class exetest {
    public static void main(String[] args) { 
        try {
            Process p = Runtime.getRuntime().exec("vi");
            p.waitFor();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

我希望Runtime.getRuntime()。exec()能够阻止,直到我完成vi,并且在此期间所有键盘输入将直接进入(RAW,因为它'已知)直到vi,所有屏幕输出都会直接返回给用户。

这就是我在C中实现它的方式:

void main() {
    system("vi");
}

更新

这实现了预期的结果,但是a)仅限于Linux / OSX(不是那么多问题,但是让它跨平台会很好),并且b)是一个可怕的kludge:

import java.io.*;

class exetest {
    public static void main(String[] args) {
        try {
            Process p = Runtime.getRuntime().exec("/bin/bash");
            OutputStream stdin = p.getOutputStream();
            PrintWriter pw = new PrintWriter(stdin);
            pw.println("vi < /dev/tty > /dev/tty");
            pw.close();
            p.waitFor();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3 个答案:

答案 0 :(得分:3)

我事先声明这是一个糟糕的混乱,但鉴于你的限制,我不确定你会找到更优雅的东西。

此外,它无法在Windows上运行。

使用bash:

class Exetest {
    public static void main(String[] args) { 
        try {
            // Create a one-liner for bash
            // Note that the trick is to do all the redirects and
            // give the name of the file in a single argument.
            String [] commandline = new String[3];
            commandline[0] = "bash";
            commandline[1] = "-c";
            commandline[2] = "vi </dev/tty >/dev/tty test.txt";
            Process p = Runtime.getRuntime().exec(commandline);
            p.waitFor();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

完成此操作后,您可以在运行时制作实际命令。

答案 1 :(得分:2)

完全等效于system("vi")

final ProcessBuilder p = new ProcessBuilder("vi");
p.redirectInput(Redirect.INHERIT);
p.redirectOutput(Redirect.INHERIT);
p.redirectError(Redirect.INHERIT);

p.start().waitFor();

(我知道这是延迟3年,但对于其他有相同问题的人)

答案 2 :(得分:-1)

嗯,首先,ProcessBuilder也在Java 6中。如果你的意思是那个版本不合适,那么我认为你需要知道两条信息。首先,运行“exernal编辑器”的主要方式可能是使用Runtime类和exec方法。我怀疑你已经在做什么了?其次,你当前的GUI,在某种程度上,有一个“事件循环”,这是一个简单但主要的线程,只做“等待事件”(如按键或“窗口事件”),调度该事件感兴趣的听众,然后等待下一个活动。外部编辑器的键盘和事件不会通过这个事件循环来进行......好吧,不管怎样,不使用像OLE API那样更复杂的东西。但是,正是“事件循环”使Java保持活跃和交互,而其他事情也在继续,例如在外部编辑器中进行编辑。我不确定你使用的GUI框架,但是大多数已经有这样的循环,你不必担心它。如果你想在你想要的这个命令行模式下,你不希望你的普通GUI运行,那么你基本上必须复制已经拥有的“事件循环”,并首先启动该线程,然后调用runtime.exec方法。合理?

FWIW,如果您没有“有源”来查看您当前正在使用的GUI框架,如果您想查看一个,只是为了了解有关事件循环的更多信息,Eclipse SWT其中有一个是“显示” “类和一些可能类似于你想要做的例子。 (注意,如果你想知道的话,SWT可以在没有剩下的Eclipse的情况下“独立运行”。

HTH