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