Runtime.exec with Unix console programs

Ever wanted to launch less or vi from a console Java program to show or edit a file, only to find that they won’t work like when you launch them from the terminal?

The problem is that these programs need to communicate with a TTY (teletypewriter) device to find the screen size and to be able to write anywhere on the terminal window. You don’t notice this when using them from a terminal because the shell sets up their stdin and stdout so they are connected to the terminal.

When you launch a program from Java using Runtime.exec the stdin and stdout are connected to pipes handled by the JVM, not to a TTY device: it is as if you tried to launch less with something like less file.txt <jvm.in >jvm.out. Needless to say that wouldn’t work even from a terminal.

What you can do is redirect the stdin and stdout streams back to the original terminal device. To find the actual TTY device we would have to call the POSIX ttyname function with JNI, but luckily that’s not necessary: we can use /dev/tty, which is the controlling terminal for the current process.

An interesting application of this is to use less as a pager to show lengthy messages to a user, like database result sets:

import java.io.OutputStream;

public class Test {
    public static void main(String[] args) throws Exception {
        Process p = Runtime.getRuntime().exec(new String[] {"sh", "-c",
                "less >/dev/tty"});
        OutputStream out = p.getOutputStream();
        out.write("Lengthy message".getBytes());
        out.close();
        System.out.println("=> "+p.waitFor());
    }
}

A penny for your thoughts

(Your email is never shared.)