java.lang.ProcessBuilder Example
In this article, we are going to discuss the ProcessBuilder
API. But first, lets understand the use of ProcessBuilder
. One can guess by its name that it has something to do with building processes.
ProcessBuilder
can be used to help create operating system processes. Before JDK 5.0, the only way to start a process and execute it, was to use the exec()
method of the java.lang.Runtime class
. Since JDK 5.0, ProcessBuilder
has added a new way of executing a command in a separate process.
The major improvement being that, it also acts as a holder for all those attributes that influence the process.
1. ProcessBuilder API
It has methods to configure the process and a start()
method to create a new Process
instance. One can re-configure the process attributes, including the process command itself and call start()
to create multiple sub processes. So the steps to run system commands is simple:
- Construct a
ProcessBuilder
object passing commands - Configure and start
ProcessBuilder
object - Assign the results to a Process object
- Read the standard output and standard error
Before I show you the examples, it is important that I brief you about the methods that help us to configure the process builder.
command(String... command)
Operating system program and arguments can be passed-in while constructingProcessBuilder
object itself. In case one wants to reuseProcessBuilder
object for a new process, the command can be reset usingcommand
.environment
This method returns process builder’s environment in form of aMap
, its initial value is a copy of the environment of the current process. One can always add new variables and it will only apply to the current process builder and not to other instances ofProcessBuilder
object.directory(file)
Using this method, one can set the working directory of the current process. By default, current working directory is set to the value returned by system propertyuser.dir
.redirectInput
Sets this process builder’s standard input destination to a file.redirectOutput
Sets this process builder’s standard output destination to a file.redirectError
Sets this process builder’s standard error destination to a file.redirectErrorStream(boolean)
If this property is set totrue
, then any error output generated by sub-processes will be merged with the standard output.inheritIO
Sets the source and destination for sub-process standard I/O to be the same as those of the current Java process.start
This method will start the process.
2. ProcessBuilder Example
In this example, I will run the echo command. The echo command and the argument values both are passed to ProcessBuilder
‘s constructor. Once ProcessBuilder
object is created, we start the process using start()
, the result of which, is a Process
object. We then wait for the process to finish using process.waitFor()
. Once the process is finished, the returned value tells us whether the process was successful. We then call process.getInputStream()
to read the process output and print its contents.
ProcessBuilderExample:
package com.javacodegeeks.process; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; public class ProcessBuilderExample { public static void main(String[] args) throws InterruptedException, IOException { ProcessBuilder pb = new ProcessBuilder("echo", "This is ProcessBuilder Example from JCG"); System.out.println("Run echo command"); Process process = pb.start(); int errCode = process.waitFor(); System.out.println("Echo command executed, any errors? " + (errCode == 0 ? "No" : "Yes")); System.out.println("Echo Output:\n" + output(process.getInputStream())); } private static String output(InputStream inputStream) throws IOException { StringBuilder sb = new StringBuilder(); BufferedReader br = null; try { br = new BufferedReader(new InputStreamReader(inputStream)); String line = null; while ((line = br.readLine()) != null) { sb.append(line + System.getProperty("line.separator")); } } finally { br.close(); } return sb.toString(); } }
Output:
Run echo command Echo command executed, any errors? No Echo Output: This is ProcessBuilder Example from JCG
3. Handle reading of sub-process output in a separate thread
In this example, I will show you how to handle sub-process IO in a separate thread so that it doesn’t cause blocking of the sub-process. We will also see you how to change the working directory.
The process to be launched is the command prompt, so we create ProcessBuilder
passing cmd.exe
. The input to the command is /C dir & echo example of & echo working dir
. It prints the current directory and then echoes “example of” and “working dir”.
Before we start the process, I would like the working directory to be changed to src
so that it prints the source directory. We can do this by calling directory(File)
and passing on the File
reference of the working directory.
I then start the process and call getInputStream()
on the returned Process
object, to get its input stream. Using the input stream we can read the process output, but instead of doing it in the same thread, I handle it in a separate thread so that any chances of it blocking the sub-process are minimized.
ProcessBuilderMultipleCommandsExample:
package com.javacodegeeks.process; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Scanner; public class ProcessBuilderMultipleCommandsExample { public static void main(String[] args) throws InterruptedException, IOException { // multiple commands // /C Carries out the command specified by string and then terminates ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/C dir & echo example of & echo working dir"); pb.directory(new File("src")); Process process = pb.start(); IOThreadHandler outputHandler = new IOThreadHandler( process.getInputStream()); outputHandler.start(); process.waitFor(); System.out.println(outputHandler.getOutput()); } private static class IOThreadHandler extends Thread { private InputStream inputStream; private StringBuilder output = new StringBuilder(); IOThreadHandler(InputStream inputStream) { this.inputStream = inputStream; } public void run() { Scanner br = null; try { br = new Scanner(new InputStreamReader(inputStream)); String line = null; while (br.hasNextLine()) { line = br.nextLine(); output.append(line + System.getProperty("line.separator")); } } finally { br.close(); } } public StringBuilder getOutput() { return output; } } }
Output:
Volume in drive C is OSDisk Volume Serial Number is 04FF-2626 Directory of C:\javacodegeeks_ws\processBuilder\src 11-02-2015 10:27 . 11-02-2015 10:27 .. 10-02-2015 10:54 com 11-02-2015 11:23 resources 0 File(s) 0 bytes 4 Dir(s) 37,247,864,832 bytes free example of working dir
4. How to redirect input and output
In this example, I want to demonstrate how to redirect input and output of the process. At the end of the example, you will learn the below mentioned:
- Adding new environment variable.
- Redirecting the process output and error to files.
- Merging error with the process output.
- Redirecting process input source to a file so that the command process can read the dos commands directly from the file.
I want to run a set of commands including a ping command. These commands are in a file called ping.bat
.
The commands make use of a couple of environment variables. The echo
command contains an environment variable called name
and the ping
command contains an environment variable called echoCount
to control the count of echo requests. When we call environment()
on the ProcessBuilder
object, we get its environment in the form of a Map
. We can then add our new environment variables to this Map
object.
Now lets come to redirecting IO. In order to direct the input and output of the process to the files, we create new file instances to represent the process output and error. We next pass the respective File
instances to the redirectOutput
and redirectError
methods. Next, we start the process, wait for it to finish and then print the file contents.
Suppose instead of having a separate error file, we want the error output to be merged to the same file which is meant for the process output, we just need to set redirectErrorStream
to true
. So in case of any errors, the error output will be merged to the process output file.
Finally, I will show how, even an input source of sub-process can be re-directed. In our example, I do it for the command sub-process so that it can read the DOS commands directly from the file itself. To demonstrate this, I create a ProcessBuilder
object using the cmd
string. I then call redirectInput(new File("src/resources/ping.bat"))
so that the input source of command process becomes the ping.bat
file itself.
ping.bat
echo Run %name% mkdir "src/resources/test" ping.exe -n %echoCount% 127.0.0.1
ProcessBuilderRedirectIOExample:
package com.javacodegeeks.process; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.Map; import org.omg.CORBA.Environment; public class ProcessBuilderRedirectIOExample { public static void main(String[] args) throws InterruptedException, IOException { ProcessBuilder pb = new ProcessBuilder("src/resources/ping.bat"); System.out.println("Configure parameters"); Map<String, String> env = pb.environment(); env.put("name", "ping command"); env.put("echoCount", "2"); System.out.println("Redirect output and error to file"); File outputFile = new File("src/resources/PingLog.txt"); File errorFile = new File("src/resources/PingErrLog.txt"); pb.redirectOutput(outputFile); pb.redirectError(errorFile); // echo Run %name% // mkdir "test" // ping.exe -n %echoCount% 127.0.0.1 Process process = pb.start(); process.waitFor(); // re-run again, should fail as test dir now already exists System.out.println("\nRerun again so that the mkdir command throws error"); process = pb.start(); process.waitFor(); System.out.println("\nPrint Output:"); printFile(outputFile); System.out.println("\nPrint Error:"); printFile(errorFile); System.out.println("\nRedirect error and run again so that error is redirected to output file"); pb.redirectErrorStream(true); File commonOutputFile = new File("src/resources/PingCommonLog.txt"); pb.redirectOutput(commonOutputFile); process = pb.start(); process.waitFor(); System.out.println("\nPrint Common Output:"); printFile(commonOutputFile); System.out.println("\nRedirect input source to a file"); pb = new ProcessBuilder("cmd"); pb.environment().putAll(env); pb.inheritIO(); pb.redirectInput(new File("src/resources/ping.bat")); process = pb.start(); process.waitFor(); } private static void printFile(File file) throws IOException { System.out.println("*********************************"); FileReader fr = new FileReader(file); BufferedReader br = new BufferedReader(fr); String line; while ((line = br.readLine()) != null) { System.out.println(line); } br.close(); fr.close(); System.out.println("*********************************"); } }
Output:
Configure parameters Redirect output and error to file Rerun again so that the mkdir command throws error Print Output: ********************************* Run ping command Pinging 127.0.0.1 with 32 bytes of data: Reply from 127.0.0.1: bytes=32 time<1ms TTL=128 Reply from 127.0.0.1: bytes=32 time<1ms TTL=128 Ping statistics for 127.0.0.1: Packets: Sent = 2, Received = 2, Lost = 0 (0% loss), Approximate round trip times in milli-seconds: Minimum = 0ms, Maximum = 0ms, Average = 0ms ********************************* Print Error: ********************************* A subdirectory or file src/resources/test already exists. ********************************* Redirect error and run again so that error is redirected to output file Print Common Output: ********************************* Run ping command A subdirectory or file src/resources/test already exists. Pinging 127.0.0.1 with 32 bytes of data: Reply from 127.0.0.1: bytes=32 time<1ms TTL=128 Reply from 127.0.0.1: bytes=32 time@ECHO OFF echo Run %name% Run ping command mkdir "src/resources/test" More? A subdirectory or file src/resources/test already exists.
5. Example of inheritIO
In this example, I call inheritIO
to redirect the sub-process IO to the standard IO of the current process. After ProcessBuilder
is created, I call on inheritIO
so that the output of sub-process gets printed to the console window.
ProcessBuilderInheritIOExample:
package com.javacodegeeks.process; import java.io.IOException; public class ProcessBuilderInheritIOExample { public static void main(String[] args) throws InterruptedException, IOException { ProcessBuilder pb = new ProcessBuilder("echo", "Hello JCG\nThis is ProcessBuilder Example"); //inherit IO pb.inheritIO(); System.out.println("Run Echo command with inheritIO set"); Process process = pb.start(); process.waitFor(); } }
Output:
Run Echo command with inheritIO set Hello JCG This is ProcessBuilder Example
Download the Eclipse Project
In this article, I have shown you various examples ofthe ProcessBuilder class.
You can download the full source code of this example here: processBuilder.zip
Hello Ram, thanks for tutorial, it’s very helpful to understand ProcessBuilder.
I follows your instruction and met a problem with 2nd example.
the code which wrote by myself give no output after run command, I checked the exit code which was 0, and I pasted and tried your code the result was the same, and I add a line “pb.inheritIO();” before Line:17@Example2 (3. Handle reading …), the it works correctly. If you got time please confirm it, thanks again.
Hello Ram, thanks for your tutorial, it’s very helpful to understanding ProcessBuilder.
I follows your instruction and met a problem with the 2nd example.
the code which wrote by myself gives no output after running command, I checked the exit code which was 0, and I pasted and tried your code, and the result was the same, and I added a line “pb.inheritIO();” before Line:17@Example2 (3. Handle reading …), then it works correctly. If you got time please confirm it, thanks again.
ProcessBuilder pb = new ProcessBuilder(“src/resources/ping.bat”);
i am not able to execute bat ..?
pb.start();
it is throwing execption