Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Execute command displaying console window and also get handle of the process

I am trying to run a command from Java that will start a process that runs for several minutes. I need to just trigger the command and get the process handle and continue with other operations in a loop. At regular intervals, I will have to monitor that the process is still active.

I also need the console window to display to show the output of the process for the user.

Currently, I have tried methods from both Runtime and ProcessBuilder classes to run my command but neither of them has helped me achieve my objective.

Sample code:

//Changing the directory and running Maven exec: java command on the POM file in that directory.

String cmd = "cd C:/Test & mvn exec:java"; 
String finalCmd = "cmd /c \""+ cmd +"\"";

Process process = Runtime.getRuntime().exec(finalCmd);
Thread.sleep(10);
boolean alive = process.isAlive();

The value of variable alive is True, but I don't see the process got started. When the program execution is complete, only then the process starts and I am not sure why that happens.

Also to display the console window, I found from google that I need to use the below command:

String finalCmd = "cmd /c start cmd.exe /c \"" + cmd + "\"";

However, with this, the process starts immediately but I do not get the process handle as I find the alive variable shows false.

Does someone know how this objective can be achieved? I am ok if it's not possible to do both at the same time but at least I need to get the process execution to start and get the handle to monitor the process state later in my code.

like image 642
Cdeez Avatar asked Feb 13 '18 11:02

Cdeez


2 Answers

Couple of things that are happening incorrectly here:

  • We need to pass our command as string tokens to the exec() command
  • We need to wait for the process to exit with process.waitFor() instead of sleeping, this will block the current thread so if you don't want that you need to execute this in another thread or use an ExecutorService.
  • Advisable to check the output value of waitFor() to see if our command executed properly (value of 0) or not (any other value, typically a positive 1 in case of unsuccessful execution)
  • Optionally (to see the output) we need to redirect the standard OUT and ERR somewhere, say print it to console(), though you could put it to a file some GUI window etc.

So at a minimum the following code should work:

Process process = Runtime.getRuntime().exec(new String[] {"cmd", "/c", "cd", "C:\\dev", "&&", "dir"});
int outputVal = process.waitFor();
boolean alive = process.isAlive();
System.out.format("alive %s, outputVal: %d\n",alive, outputVal);

Further suggestions:

  • use ProcessBuilder instead of runTime.exec(), it allows more control and is the recommended way since JDK 1.5
  • read the inputStream

So the code will look some thing like this:

    List<String> cmdList = Arrays.asList("cmd", "/c", "cd", "C:\\dev", "&&", "dir");
    ProcessBuilder pb = new ProcessBuilder(cmdList);
    pb.redirectErrorStream(true); //redirect STD ERR to STD OUT
    Process process = pb.start();
    try (final BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
        String line = null;
        while ((line = br.readLine()) != null) {
              System.out.println("std-out-line: " + line);
        }
    }
    int outputVal = process.waitFor();
    System.out.format("outputVal: %d\n", outputVal);

Since waitFor() is a blocking call, you can execute this in a separate thread or using an executorService. Sample code here:

    final StringBuffer outputSb = new StringBuffer();
    ExecutorService executorService = null;
    try {
        executorService = Executors.newSingleThreadExecutor();
        final Future<Integer> future = executorService.submit(new Callable<Integer>() {

            @Override
            public Integer call() throws Exception {
                try (final BufferedReader br = new BufferedReader(
                        new InputStreamReader(process.getInputStream()))) {
                    String line = null;
                    while ((line = br.readLine()) != null) {
                        outputSb.append("std-out-line: ");
                        outputSb.append(line);
                        outputSb.append('\n');
                    }
                }
                int exitValue = process.waitFor();
                System.out.format("exitValue: %d\n", exitValue);

                return exitValue;
            }
        });

        while (!future.isDone()) {
            System.out.println("Waiting for command to finish doing something else..");
            Thread.sleep(1 * 1000);
        }

        int exitValue = future.get();
        System.out.println("Output: " + outputSb);

    } finally {
        executorService.shutdown();
    }
like image 174
Deepak Avatar answered Nov 14 '22 06:11

Deepak


Here's a solution that uses WMIC.

public static void main( String[] args ) throws Exception {

    // Vars
    Process process;
    String output;

    // Execution
    process = Runtime.getRuntime().exec("cmd /c wmic process call create calc.exe | findstr ProcessId");
    output = readTrimmedOutput(process.getInputStream());
    System.out.println("Output from command: " + output);

    // Basic string manipulation to get process id
    String str_proc_id = output.split(" = ")[1].replace(";","");
    System.out.println("ProcessId is: " + str_proc_id);

    // Some thread delay that you can comment/uncomment for testing if running or not
    Thread.sleep(5000);

    // Finding if process is still running
    process = Runtime.getRuntime().exec("cmd /c wmic process get processid | findstr " + str_proc_id);
    output = readTrimmedOutput(process.getInputStream());

    boolean isRunning = output.contains(str_proc_id);
    System.out.println("Is process still running? " + isRunning);

}

private static String readTrimmedOutput(InputStream is) throws Exception {
    BufferedReader breader = new BufferedReader(new InputStreamReader(is));
    String line = breader.readLine();
    return line != null ? line.trim() : "";
}

Sample output

Output from command: ProcessId = 6480;
ProcessId is: 6480
Is process still running? true

For showing/displaying cmd console change some lines to:

// Execution
String your_command = "cmd.exe /c \"dir\"";
process = Runtime.getRuntime().exec("cmd /c wmic process call create \"" + your_command + "\" | findstr ProcessId");

References:

https://msdn.microsoft.com/en-us/library/aa394531(v=vs.85).aspx

https://www.computerhope.com/wmic.htm

like image 1
Ezekiel Baniaga Avatar answered Nov 14 '22 08:11

Ezekiel Baniaga