Search code examples
javaprocessbuilder

Invoking cmd commands from Java using processbuilder


I am trying to invoke cmd commands from Java using processbuilder. However I am facing few issue.

  1. When I use List built using Arrays.asList, the application hangs infinitely after executing br.readline() (not because of loop but at readLine method). Using String array gives the output. I checked the grepcode and it looks like it should not have issue for any of these as both of them are later converted back to array when start method is invoked from processbuilder. (Link: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b27/java/lang/ProcessBuilder.java#ProcessBuilder ). I am not sure what is causing this anomaly.

  2. This program sometimes does not give correct output. If I use process.destroy() method and get the exitValue, it shows 1. If I comment out process.destroy() method, exception comes that process has not exited. I guess it may be thread race condition. However, there is not effect of process.waitFor(). It sometimes still produces the wrong output. How can we test these situation and find out real reason of the issue?

  3. I need to invoke few commands inside cmd. However the array elements act as parameter for previous one. For example, if I make array with elemets being cmd /C dir whoami. Then this produces wrong output as whoami acts as parameter for dir. What shall be the correct way to independently provide commands to cmd?

Below is the sample code having same issues:

   import java.io.BufferedReader;
   import java.io.InputStreamReader;
   import java.util.Arrays;
   import java.util.List;
   public class Sample {
       public static void main(String[] args) throws Exception {
        //List<String> commandList = Arrays.asList("cmd.exe","dir");
        String[] commandList = {"cmd.exe", "/C", "dir"};
        //String[] commandList = {"cmd.exe", "/C", "dir", "whoami"};
        //String[] commandList = new String[] {"cmd.exe", "/C", "dir"};
        ProcessBuilder processBuilder = new ProcessBuilder(commandList);
        Process process = processBuilder.start();
        //process.waitFor();
        BufferedReader iReader = new BufferedReader(
                             new InputStreamReader(process.getInputStream()));
        String tempStr= "";
        StringBuffer buffer = new StringBuffer();
        while((tempStr = iReader.readLine())!=null){
            buffer.append(tempStr+System.lineSeparator());
        }
        System.out.println(buffer.toString());
        process.destroy();
        int exitValue = process.exitValue();
        System.out.println(exitValue);
    }
   }

Solution

    1. Arrays.asList("cmd.exe","dir") has two List elements. ("cmd.exe", "dir") is not the same as ("cmd.exe", "/C", "dir"). It’s probably hanging because you are ignoring the error output that contains the error message describing your error. You can fix this with processBuilder.redirectError(ProcessBuilder.Redirect.INHERIT); (which is a good idea in general, unless you plan to read the error stream some other way).
    2. Remove process.destroy() and replace process.exitValue() with process.waitFor(). Do not call process.waitFor() before you have read all of the output; waitFor() waits for the process to end, and you can’t read output from a process that is no longer running.
    3. You may be able to chain commands using && between them; for instance: new ProcessBuilder("cmd.exe", "/C", "dir && date /t"). If that does not work, you can try creating a temporary .bat file and passing it to cmd /c. If that isn’t acceptable, you probably will have to create a separate process for each command you want to run.

    By the way, StringBuffer is obsolete. Use StringBuilder instead, as it doesn’t have the overhead of unnecessary synchronization.