0

I have a Java program that executes sh in interactive mode as a subprocess. The input is copied from System.in and output is copied to System.out. Everything works fine except that when running commands such as pwd in this interactive shell the output appears in a wrong order, for example:

$ pwd
$ /home/viz/workspace

instead of

$ pwd
/home/viz/workspace
$

The difference is that in the first case the prompt $ is printed before the output from pwd.

Any ideas why does it happen and how to fix it?

Here is the code:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

class StreamCopier implements Runnable {
    private InputStream in;
    private OutputStream out;

    public StreamCopier(InputStream in, OutputStream out) {
        this.in = in;
        this.out = out;
    }

    public void run() {
        try {
            int n;
            byte[] buffer = new byte[4096];
            while ((n = in.read(buffer)) != -1) {
                out.write(buffer, 0, n);
                out.flush();
            }
            out.close();
        }
        catch (IOException e) {
            System.out.println(e);
        }
    }
}

public class Test {
    public static void main(String[] args)
            throws IOException, InterruptedException {
        Process process = Runtime.getRuntime().exec("sh -i +m");
        Thread outThread = new Thread(new StreamCopier(
                process.getInputStream(), System.out));
        outThread.start();
        Thread errThread = new Thread(new StreamCopier(
                process.getErrorStream(), System.err));
        errThread.start();
        Thread inThread = new Thread(new StreamCopier(
                System.in, process.getOutputStream()));
        inThread.start();
        process.waitFor();
        outThread.join();
        errThread.join();
        inThread.join();
    }
}
vitaut
  • 49,672
  • 25
  • 199
  • 336
  • What happens if you run a different shell? – dacwe Nov 18 '10 at 15:57
  • @dacwe: I could only make `sh` work so far. Tried `bash` but it is stopped when trying to do IO. Probably it needs some other options (`+m` doesn't help). – vitaut Nov 18 '10 at 16:10

1 Answers1

1

Because standard error and standard output go to different threads which print the read data asynchronously. It might work, if you redirect 2>&1, but I'm not sure it will work with Runtime.exec (may cause "file not found - 2>&1"). So you can make a shell script which calls sh and redirects its output, and call that script using Runtime.exec:

#!/bin/sh
sh -i +m 2>&1

and

Runtime.exec("customsh"); 
khachik
  • 28,112
  • 9
  • 59
  • 94
  • But both `$` and `/home/viz/workspace` should be outputted on standard output? (so no race there..) – dacwe Nov 18 '10 at 16:14
  • @dacwe No. Try from the command-line `sh 1>temp.log` and then type `pwd`. You will see the command-line prompt but not pwd output, it will go to `temp.log`. – khachik Nov 18 '10 at 16:24
  • Nice! Then there is vitaut solution! – dacwe Nov 18 '10 at 16:26