ProcessBuilder

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:

  1. Construct a ProcessBuilder object passing commands
  2. Configure and start ProcessBuilder object
  3. Assign the results to a Process object
  4. 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 constructing ProcessBuilder object itself. In case one wants to reuse ProcessBuilder object for a new process, the command can be reset using command.
  • environment
    This method returns process builder’s environment in form of a Map, 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 of ProcessBuilder 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 property user.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 to true, 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:

  1. Adding new environment variable.
  2. Redirecting the process output and error to files.
  3. Merging error with the process output.
  4. 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.

Download
You can download the full source code of this example here: processBuilder.zip

Ram Mokkapaty

Ram holds a master's degree in Machine Design from IT B.H.U. His expertise lies in test driven development and re-factoring. He is passionate about open source technologies and actively blogs on various java and open-source technologies like spring. He works as a principal Engineer in the logistics domain.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

3 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Wayne
Wayne
6 years ago

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.

Wayne
Wayne
6 years ago

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.

Ankush
Ankush
5 years ago

ProcessBuilder pb = new ProcessBuilder(“src/resources/ping.bat”);
i am not able to execute bat ..?
pb.start();
it is throwing execption

Back to top button