1

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);
    }
   }

1 Answer 1

2
  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.

Sign up to request clarification or add additional context in comments.

4 Comments

Ah!!! I didn't realize that issue was coming through missing "/C" switch which terminates automatically after completion. Thanks for "&&" command in cmd. Unfortunately, the commands are not fixed but generated dynamically based on criteria. So script wasn't much viable here. I will try with repositioning the process.waitFor and get back to you. Meanwhile, is there any method to test the thread race around or create scenario where process ends too quickly or takes very long time so we can check how it works in end scenarios.
You can write dynamic commands to a new file with a ".bat" extension and pass that file name to cmd /c. Files.createTempFile and Files.write are useful for that.
Yes, that involves creating a temporary file. That idea was also put forward, but my team doesn't approve for IO. And second issue was getting location access for temp storage. But as long "&&" works it's good. Otherwise I will try to run multiple processes one after another. Last thing, before this program runs, we are checking host OS. So we will be invoking terminal in case its linux OS. I have tried && command in online terminal and it looks good but I need to be sure it works with all commands instead of basic ls, whoami etc. It will be debian or its variants
I believe I might not have requisite permissions to accept answer or might have left it to come back and discuss more. I just revisited the post while looking back my olde code. Thanks for the help!!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.