mp3por
mp3por

Reputation: 1852

ProcessBuilder handling output

I am writing a program to read commands from a file, execute them and print the result of each command.

This is what i have: import java.io.*;

public class StatsGenerator {
    public static void main(String [] args) throws IOException {
        ProcessBuilder builder = new ProcessBuilder( "/bin/bash" );
        Process p = builder.start();

        // get output from the process
        InputStream is = p.getInputStream();
        InputStreamReader isr = new InputStreamReader(is);
        BufferedReader processOutput = new BufferedReader(isr);

        InputStream errorStream = p.getErrorStream();
        InputStreamReader inputStreamReader = new InputStreamReader(errorStream);
        BufferedReader processErrorOutput = new BufferedReader(inputStreamReader);

        // get input to the process
        BufferedWriter processInput = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));

        // get commands to execute
        File f = new File("commands.txt");
        FileReader fileReader = new FileReader(f);
        BufferedReader commandsReader = new BufferedReader(fileReader);
        String command, output;
        while((command = commandsReader.readLine()) != null) {
            System.out.printf("Output of running %s is:\n", command);
            processInput.write(command);
            processInput.newLine();
            processInput.flush();
            while (processErrorOutput.ready() && (output = processErrorOutput.readLine()) != null) {
                System.out.println(output);
            }
            while ((output = processOutput.readLine()) != null) {
                System.out.println(output);
            }
        }

        // close process
        processInput.write("exit");
        processInput.newLine();
        processInput.flush();

        // close streams
        commandsReader.close();
        processErrorOutput.close();
        processInput.close();
        processOutput.close();

    }
}

commands.txt

java Solve problems/problem01.txt
java Solve problems/problem02.txt
java Solve problems/problem03.txt

However this runs and outputs the result of the first command but gets stuck on the second one ... ( and I know that Solve can solve the second one )

enter image description here

What am i doing wrong ?

EDIT1:

Turns out the "Exception in thread 'main'" error from the picture is due to me pressing the CMD+C. This still does not explain why it stops outputting.

Upvotes: 2

Views: 3436

Answers (2)

Simon
Simon

Reputation: 140

And yet better is to run error and stream reading in separate threads in order to provide make program able to do something else, terminate process for instance

Upvotes: 0

laune
laune

Reputation: 31300

The problem I see here is that you create a process running a shell (OK), get hold of the input and output streams of that process (OK), read a command from the file (OK) and feed it to the process (OK). Then you keep reading output lines, which succeeds while the the first Java program executes and produces output.

Then the

while ((output = processOutput.readLine()) != null) { ...

blocks as there is neither another line nor EOF.

You can fix this by spawning a thread to read and print processOutput.

Another option (which I'd very much prefer) is to create one process per command. As far as I can see you don't even need a shell: you could execute java SolveProblem ... right away. (Unices have been built for efficient subprocess creation, and don't think the shell does it differently, so there's no additional overhead to be afraid of.)

Just two hints for calling java without a shell: make sure to use the full path name and split the command line into tokens.

Edit And here it is, just using a String[] instead of your text file containing commands.

for( String cmd: new String[]{ "java Y aaa", "java Y bbb","java Y ccc" } ){
     String[] toks = cmd.split( "\\s" );
     ProcessBuilder builder = new ProcessBuilder( toks );
     Process p = builder.start();

     // get output from the process
     InputStream is = p.getInputStream();
     InputStreamReader isr = new InputStreamReader(is);
     BufferedReader processOutput = new BufferedReader(isr);

     InputStream errorStream = p.getErrorStream();
     InputStreamReader inputStreamReader = new InputStreamReader(errorStream);
     BufferedReader processErrorOutput = new BufferedReader(inputStreamReader);
     System.out.println("Executing " + cmd);
     String output;
     while( processErrorOutput.ready() &&
        (output = processErrorOutput.readLine()) != null) {
     System.out.println(output);
     }
     while ((output = processOutput.readLine()) != null) {
     System.out.println(output);
     }
     processErrorOutput.close();
     processOutput.close();
}

Output (silly Y prints argument three times):

Executing java Y aaa
1aaa
2aaa
3aaa
Executing java Y bbb
1bbb
2bbb
3bbb
Executing java Y ccc
1ccc
2ccc
3ccc

Another Edit If a marker line is inserted into the process output and the read loop checks the lines for this marker, the program can be used as it is (except for some corrections for closing processInput):

processInput.write(command + "; echo xxxEOFxxx");
//...
while ((output = processOutput.readLine()) != null
       && ! "xxxEOFxxx".equals(output)) {
    System.out.println(output);
}

Although I dislike the use of "magic strings" in this way it may be permissible here as you know the set of output lines of program Solve.

Upvotes: 2

Related Questions