1

Try to figure out what I am missing (I'm still quite green on some advanced programming concepts, this is probably why I don't fully grasp my issue): I am creating an app which allows user, among other things, to open specific documents, apps or command line instructions the user types in.

Where I fail to have it working is to try to create a ProcessBuilder object to test a user-typed-in command line (macOS and Windows complaint). For a general purpose command line, e.g. ls (macOS) or dir (Windows), my code is doing fine. But when I try a command line which would require user interaction, I fail to have the output/input/error streams to properly work. I saw many related posts about issues, but none worked for me, so I hope I didn't miss out on one (sorry if I did).

Post "Java ProcessBuilder process waiting for input" seemed the closest for me but I get grief on line new Thread(new StreamGobbler("in", out, inStream)).start(); from my IDE stating that a non-static variable this cannot be referenced from a static method.

Here is the code that I got to finally run. It simply stops at value = is.read(); when prompt reaches the last character to display to user (EOF maybe? In may case, it asks user whether the target is a file or a folder), which I understand is because the input stream listening is not actually finished as such.

Please note that the "xcopy" command is ONLY to illustrate my issue, the actually command line can be virtually any command line the user would enter in a field within my application.

class Test {

public static void main(String[] args) {
    try {
        String command = "xcopy <file1.txt> <file2.txt>  /L /Y";
        //Following line is specific to Windows, where "cmd /c" should be
        //pre-appended to have command line to properly work.
        ProcessBuilder pb = new ProcessBuilder("cmd", "/c", command);
        pb.redirectErrorStream(true);
        Process p = pb.start();
        InputStreamPass isc = new InputStreamPass(p.getInputStream());
        InputStreamPass errsc = new InputStreamPass(p.getErrorStream());
        OutputStreamExport ose = new OutputStreamExport(p.getOutputStream());
        ose.run();
        errsc.run();
        isc.run();
        int exitCode = p.waitFor();
        System.out.println("Exit code: " + exitCode);
    } catch (SecurityException | IOException | InterruptedException | NullPointerException ex) {
        System.err.println(ex);
    }
}

private static class InputStreamPass implements Runnable {

    private final InputStream is;
    InputStreamPass(InputStream is) {
        this.is = is;
    }

    @Override
    public void run() {
        try {
            String message = "";
            int value = is.read();
            while (value != -1) {
                System.out.print((char)value);
                message += (char)value;
                //Code blocks at following line when reaches
                //end of prompt, waiting for user input, so value never
                //gets to be assigned the final is.read() value
                value = is.read();
            }
            System.out.println(message);
        } catch (IOException ex) {
            System.err.println(ex);
        }
    }

}//private static class InputStreamConsumer extends Thread

private static class OutputStreamExport implements Runnable {

    private final OutputStream ots;
    OutputStreamExport(OutputStream ots) {
        this.ots = ots;
    }

    @Override
    public void run() {
        try {
            ots.flush();
        } catch (IOException ex) {
            System.err.println(ex);
        }
    }

}//private static class InputStreamConsumer extends Thread

And here is the output (localized form):

    Est-ce que file2.txt désigne un nom de fichier ou un nom de répertoire de la destination (F = fichier, R = répertoire)ÿ?
    -or rough back-translation:
    Does file2.txt relates to the filename or the foldername of the target (F = file, D = directory)?

Any help is more than welcome (I'm already 3 days searching and testing to no avail) :)

4
  • if you're copying files you should use copy instead of xcopy Commented Aug 28, 2017 at 15:51
  • Sure, but the "xcopy" is only for illustrating and issue when user is required interaction, not so much about the copy itself :) In other words, what if a command line is prompting for data (and therefore how to properly listen to such prompts so we can display it through the hosting app). Commented Aug 28, 2017 at 16:53
  • xcopy is waiting for input on its stdin, which is (the other end of) the pipe Process.getOutputStream. Nothing in your code ever writes anything to that stream, so xcopy waits forever. If you do want to write something, like in this case a line containing D, then put code in your program that does that. Commented Aug 28, 2017 at 18:37
  • Thanks for giving a clue, Dave. I am not quite sure to understand how to fix it though (if it can): I went a step back to Post link, where we do check whether user needs to input something through Scanner class, but I actually get the same behaviour: my program doesn't seem to be able to take whatever is output and get the Scanner to receive user input. It simply blocks on inScanner.nextLine() if this is the last output line and command line is waiting for user input. Commented Aug 29, 2017 at 11:04

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.