如何在commons exec下从Java内部启动VI?

如何在commons exec下从Java内部启动VI?,java,shell,command,vi,Java,Shell,Command,Vi,我希望能够从Java程序中启动VI,并等待用户退出VI后再继续。以下是我目前拥有的代码片段: ... String previewFileName="test.txt"; // the file to edit CommandLine cmdLine = new CommandLine("/usr/bin/vi"); cmdLine.addArgument(previewFileName); cmdLine.addArgument(">/dev/tty"); cmdLine.add

我希望能够从Java程序中启动VI,并等待用户退出VI后再继续。以下是我目前拥有的代码片段:

...    
String previewFileName="test.txt"; // the file to edit
CommandLine cmdLine = new CommandLine("/usr/bin/vi");
cmdLine.addArgument(previewFileName);
cmdLine.addArgument(">/dev/tty");
cmdLine.addArgument("</dev/tty");

Executor executor = new DefaultExecutor();
try
{
    DefaultExecuteResultHandler resultHandler = new ResetProcessResultHandler(cmdLine);
    executor.execute(cmdLine, resultHandler);
} catch (IOException e)
{
    throw new Error("Cannot execute command: /usr/bin/vi " + previewFileName, e);
}
log.info("waiting...");
cmdLine.wait();
log.info("...done");
...

private class ResetProcessResultHandler extends DefaultExecuteResultHandler
{
    private final CommandLine mCommandLine;
    public ResetProcessResultHandler(CommandLine pCommandLine)
    {
        mCommandLine = pCommandLine;
    }
    public void onProcessComplete(int exitValue)
    {
        log.info("Command complete  rc(" + exitValue + ")");
        if (exitValue != 0)
        {
            throw new RuntimeException("VI command error [rc=" + exitValue + "] " );
        }
        mCommandLine.notify();
    }
    public void onProcessFailed(ExecuteException e)
    {
        if (e.getExitValue() != 0)
        {
            log.error("launch VI error " + e.toString());
            throw new RuntimeException("VI command failed [" + e.getCause() + "] ");
        }
        else
        {
            log.info("VI complete   rc(" + e.getExitValue() + ")");
        }
        mCommandLine.notify();
    }
}
但后来我看到屏幕上画的好像VI已经开始了;VI不读我输入的字符

所以。。。从/dev/tty重定向并没有奏效

一定有人做过这件事-救命

谢谢


马克

我不知道如何使用commons exec

但是标准Java应该是符合以下原则的东西

String[] command = {"/usr/bin/vi", "test.txt"};
Process vimProcess = Runtime.getRuntime().exec(command);
vimProcess.waitFor();
这将导致当前线程等待进程完成。你也可以使用 vimProcess.getInputStream()、getOutputStream()和getErrorStream()将这些文件重定向到日志文件或您希望它去的任何地方

请参阅此处了解更多详细信息。


希望这会有所帮助。

当Java通过
Runtime.exec()
运行程序时(这就是commons exec最后所做的),它将程序的输入、输出和错误流作为输入/输出流连接到Java应用程序。这样的流肯定不是终端,例如,您不能移动其中的文本光标(因为它没有任何光标)、更改文本颜色,或者检测是否按下了
Shift
键(因为它只是一个字节流,而不是物理键盘)。因此,像
vi
这样的交互式应用程序在终端这样的条件下无法真正运行

顺便说一下,我不确定您提供的命令行参数是由shell解析还是直接传递给程序。在后一种情况下,重定向到
/dev/tty
可能无法工作,即使Java有办法以某种方式允许程序将Java连接的流替换为其他流

另一方面,为什么要从Java程序内部运行
vi
,似乎有点奇怪


因此,我想最好的解决方案是执行一个终端仿真器,比如
konsole
gnome-terminal
xterm
,并让它通过在命令行上传递相应的参数来运行vi(例如
konsole-evi
)。在这种情况下,终端的窗口应该弹出,并且
vi
可以在其中工作。当然,如果您使用的是无头服务器,那么它将不起作用,但是运行
vi
无论如何都不会有用。

但是,由于Java 1.7,您可以使用下一个示例透明地重定向,并具有完整的控制台功能

System.out.println("STARTING VI");
 ProcessBuilder processBuilder = new ProcessBuilder("/usr/bin/vi");
 processBuilder.redirectOutput(ProcessBuilder.Redirect.INHERIT);
 processBuilder.redirectError(ProcessBuilder.Redirect.INHERIT);
 processBuilder.redirectInput(ProcessBuilder.Redirect.INHERIT);

 Process p = processBuilder.start();
  // wait for termination.
  p.waitFor();
System.out.println("Exiting VI");

这将允许您为JVM 1.7+透明地打开VI。

这将不起作用,因为VI没有被带到前台并连接到用户的tty(这显然是问题真正要问的问题)。我尝试了一些事情,例如将
系统.in
泵送到子进程的
输出流
(这是子流程的标准输入),等等。它基本上不起作用。很遗憾,因为这是我真正想做的事情。@ChristopherSchultz不幸的是,您需要同时生成输入和使用输出-否则,由于缓冲区已满,进程可能会冻结。是的,我知道您必须泵送所有流。它仍然不起作用。我已经放弃从Java.F中执行它很重要的是,从C做这件事就像fork+exec一样简单。我没有费心编写JNI来做这样一个fork+exec来让它工作,因为大部分JNI都不值得这么麻烦。
System.out.println("STARTING VI");
 ProcessBuilder processBuilder = new ProcessBuilder("/usr/bin/vi");
 processBuilder.redirectOutput(ProcessBuilder.Redirect.INHERIT);
 processBuilder.redirectError(ProcessBuilder.Redirect.INHERIT);
 processBuilder.redirectInput(ProcessBuilder.Redirect.INHERIT);

 Process p = processBuilder.start();
  // wait for termination.
  p.waitFor();
System.out.println("Exiting VI");