I would like to be able to launch VI from within my Java program and wait for the user to quit VI before proceeding. Here's the code snippet that I have currently:
...    
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();
    }
}
I receive output:
Vim:  output is not to a terminal
Vim:  input is not from a terminal
But then I see the screen painted as if VI had started; and VI doesn't read characters I type.
So ... redirecting from /dev/tty isn't doing the trick.
Someone must have done this before - help!
Thanks,
Mark
However since Java 1.7 you can use the next example to transparently redirect and have full console functionality
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");
This will allow you to open VI transparently for JVM 1.7+.
When Java runs a program via Runtime.exec() (and this is what commons-exec does in the end), it connects the program's input, output and error streams to your Java app as input/output streams. Such a stream is certainly not a terminal, you can't for example move the text cursor in it (since it doesn't have any), change text colors, or detect if Shift key is pressed (since it's just a stream of bytes and not a physical keyborad). So, an interactive app like vi can't really function under such conditions like in a terminal.
By the way, I'm not sure if the command line args you supply are parsed by the shell or passed directly to the program. In the latter case your redirection to /dev/tty couldn't possibly work even if there was a way for Java to somehow allow the program to replace Java's connected streams with something else.
As an aside, it seems a bit strange why you would like to run vi from inside a Java program.
So I guess the best solution is to execute a terminal emulator like konsole or gnome-terminal or xterm and let it run vi by passing corresponding argument on its command line (e.g. konsole -e vi). In this case the terminal's window should pop up and vi could function inside it. Of course, it won't work if you're on a headless server, but then running vi can't be useful anyway.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With