2

I have been playing with java processes for a while and am stuck. What i want to do is run multiple system commands at the same time and print their output to console.

For example, ls -l ; cat someFile ; quit ; grep foo someOtherFile should all be running at the same time. I have read somewhere that the output of these commands should be intermixed. In addition, if there's a quit command anywhere in the string, continue executing other commands and then exit.

Right now, they are executing sequentially. How do I run them concurrently and print their output as it arrive.

String st = "ls -l ; cat someFile ; quit ; grep foo someOtherFile";

String[] rows = st.split(";");

String[][] strArray = new String[rows.length][];
int index = 0;
for(int i = 0; i < rows.length; i++) {
    rows[index] = rows[index].trim();
    strArray[index] = rows[index].split(" ");
    index++;
}
for(int i = 0; i < strArray.length; i++) {
    if(rows[i].equalsIgnoreCase("quit")) {
        System.out.println("Abort");
        break;
    }
    if(rows[i].equals("")) {
        continue;
    }
    ProcessBuilder pb = new ProcessBuilder(strArray[i]);
    pb.redirectErrorStream(true);
    Process process = pb.start();

    InputStream is = process.getInputStream();
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);

    String line;
    while ( (line = br.readLine()) != null) {
        System.out.println(line);
    }
    br.close();
}
devDude
  • 17
  • 3
  • 2
    I think that requires some sort of [Threading](http://docs.oracle.com/javase/tutorial/essential/concurrency/) – chickity china chinese chicken Oct 17 '16 at 17:19
  • Have you tried threads? if not try it please, if any issues in that.. let us know – Shreyas Sarvothama Oct 17 '16 at 17:30
  • Most of your commands will not run. For instance, `new ProcessBuilder("ls -l")` will try to run an executable program whose file name contains five characters: `l`, `s`, space, hyphen, `l`. You could of course split each command string on whitespace, but if the command itself contains spaces in a quoted argument (like `grep 'Hello World' Test.java`) it will not work properly. In general, it’s better for commands to be split into their arguments before trying to invoke them, such as `new ProcessBuilder("grep", "Hello World", "Test.java")`. – VGR Oct 17 '16 at 18:19

3 Answers3

1

Just put the guts of your loop inside the run() function of a new thread, and each iteration of the loop will run in a separate thread:

new Thread() {
    public void run() {
        // loop guts go here
    }
}.start();

You may have to declare a few variables as finals in order to access them inside this anonymous inner class.

clemep
  • 124
  • 11
0

You should look to some documentation about concurrency, threads an such http://docs.oracle.com/javase/tutorial/essential/concurrency/.

Here an edit to your code that may work, not tested.

    String st = "ls -l ; cat someFile ; quit ; grep foo someOtherFile";

    String[] rows = st.split(";");

    String[][] strArray = new String[rows.length][];
    int index = 0;
    for(int i = 0; i < rows.length; i++) {
        rows[index] = rows[index].trim();
        strArray[index] = rows[index].split(" ");
        index++;
    }

    List<Thread> threads = new ArrayList<Thread>();

    for(int i = 0; i < strArray.length; i++) {
        if(rows[i].equalsIgnoreCase("quit")) {
            System.out.println("Abort");
            break;
        }
        if(rows[i].equals("")) {
            continue;
        }

        final int iForThread = i;

        Thread thread = new Thread() {

            public void run(){
                try{
                    ProcessBuilder pb = new ProcessBuilder(strArray[iForThread]);
                    pb.redirectErrorStream(true);
                    Process process = pb.start();

                    InputStream is = process.getInputStream();
                    InputStreamReader isr = new InputStreamReader(is);
                    BufferedReader br = new BufferedReader(isr);

                    String line;
                    while ( (line = br.readLine()) != null) {
                        System.out.println(line);
                    }
                    br.close();
                }catch(IOException e){
                    //Log some awesome error
                    //Clean up
                    //Do whatever
                }
            }
        };

        threads.add(thread);

    }

    final CyclicBarrier gate = new CyclicBarrier(threads.size() + 1); //+1 is a tip from other post

    for(Thread thread : threads){
        thread.start();
    }

    try {
        gate.await();
        System.out.println("all threads started");
    } catch (InterruptedException | BrokenBarrierException e) {
        e.printStackTrace();
        /* RONALDO OF ERROS
         * MESSI OF HANDLERS*/
    }

}

It creates an tread and executed it at the spot. I if you are just messing around I think this enough.

Edit: Added start threads at "same time"

Based on: How to start two threads at "exactly" the same time

See:https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CyclicBarrier.html

Community
  • 1
  • 1
Adilson Cabral
  • 548
  • 5
  • 12
  • I ran some commands and was expecting the output of those commands to be intermixed. – devDude Oct 19 '16 at 02:43
  • I see what you mean,my guess is that maybe the execution time is of the commands is not long enough to the effect. Try to start them all at the same time. SEE:https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CyclicBarrier.html SEE: http://stackoverflow.com/questions/3376586/how-to-start-two-threads-at-exactly-the-same-time – Adilson Cabral Oct 19 '16 at 09:17
  • edited the answer to express the idea, IT IS NOT TESTED – Adilson Cabral Oct 19 '16 at 09:51
  • Thanks. A noob question for you....can you please explain why you defined ``thread``` like this. I understand what's going on here or the what code does. Just never seen an object defined like these. – devDude Oct 19 '16 at 23:39
  • What I used is called Anonymous Classes. I used this approach because because it was an example and is simple to see what gonna be executed. I don't have any amazing technical reason for this, but it may exist one :p SEE:https://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html – Adilson Cabral Nov 02 '16 at 18:50
0

You can try code similar to this:

// command list
String st = "ls -la; cat someFile";

String[] commands = st.split(";");

for (int i = 0; i < commands.length; i++) {
    String currentCommand = commands[i].trim();

    System.out.println("Command: " + currentCommand);

    Thread thread = new Thread(() -> {
        try {
            ProcessBuilder command = new ProcessBuilder(currentCommand);
            Process process = command.start();
            InputStream is = process.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));

            String line;

            while((line = br.readLine()) != null)    {
                System.out.println(line);
            }
            br.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    });

    thread.start();
}

Disclaimer: not tested on a Linux machine. Windows machines will probably not work. See this link regarding Windows command line process execution.

darioo
  • 46,442
  • 10
  • 75
  • 103