Boost.Process is a header-only library. It comes with a convenience header file which is the only one you need to include to make use of all library features:
#include <boost/process.hpp>
All examples in this tutorial assume this header file is included.
Note | |
---|---|
The header file |
Most definitions can be found in one namespace:
boost::process
For example, the most important function execute
to start a program is defined in boost::process
.
There is a second namespace used by Boost.Process:
boost::process::initializers
This namespace is used for initializers. Initializers are the parameters
passed to execute
.
As you commonly use initializers provided by Boost.Process, you need to use
this namespace, too.
The function Boost.Process provides to start a program is execute
.
You need to pass at least one initializer as a parameter. Think of initializers
as named parameters. With the initializer run_exe
you refer to the program you want to start:
execute(run_exe("test.exe"));
run_exe
expects an exact filename. For instance, you can't leave out the file extension.
run_exe
does not automatically search for a program either. In the example above
Boost.Process expects to find test.exe in
the current work directory.
You can refer to programs in other directories with an absolute or relative
path. It is also possible to use boost::filesystem::path
:
boost::filesystem::path exe = "../test.exe"; execute(run_exe(exe));
Boost.Process provides two utility functions to lookup executables: Call
search_path
if you want to find an executable in the directories of the environment variable
PATH. Or call shell_path
for the system's shell.
If you have a long-running program starting other programs with execute
, you want to be careful
cleaning up resources. Resources are for example entries in the system's
process table on POSIX and handlers to child processes on Windows.
The only reliable cross-platform solution to clean up resources is to call
wait_for_exit
:
child c = execute(run_exe("test.exe")); wait_for_exit(c);
wait_for_exit
is a blocking function. The function returns when the child process exits.
Calling wait_for_exit
guarantees that all resources are cleaned up.
There are other platform-specific solutions to clean up resources. Boost.Process
provides the macros BOOST_POSIX_API
and BOOST_WINDOWS_API
if you
need to distinguish between platforms. On POSIX you can call signal
for example to ignore SIGCHLD:
#if defined(BOOST_POSIX_API) signal(SIGCHLD, SIG_IGN); #endif execute(run_exe("test.exe"));
If SIGCHLD is ignored, a child process isn't added to the system's process
table. There is no need then to call wait_for_exit
.
It is important to ignore SIGCHLD before the child process exits though.
For instance, call signal
before you call execute
as in the example above.
{ child c = execute(run_exe("test.exe")); }
On Windows child
is a
movable but non-copyable type. The destructor closes handles to the child
process when the instance of child
goes out of scope. On Windows it's not strictly required to call wait_for_exit
to clean
up resources.
Boost.Process provides two initializers to detect errors. Use set_on_error
if you want execute
to return an error:
boost::system::error_code ec; execute( run_exe("test.exe"), set_on_error(ec) );
Use throw_on_error
if you want execute
to throw an exception:
execute( run_exe("test.exe"), throw_on_error() );
The type of the exception thrown by throw_on_error
is boost::system::system_error
.
Note | |
---|---|
On POSIX |
Use the initializer set_args
to set command line arguments:
std::vector<std::string> args; args.push_back("test.exe"); args.push_back("--foo"); args.push_back("/bar"); execute(set_args(args));
If set_args
is used, run_exe
may be omitted. The first command line argument must then refer to the program
to start.
Alternatively use the initializer set_cmd_line
to set the command line:
execute( run_exe("test.exe"), set_cmd_line("test --foo /bar") );
You can use set_cmd_line
to set a command line like you would if you started the program in the shell.
Just like in the shell you must start the command line with the name of the
program.
You must not omit run_exe
if you use set_cmd_line
.
Note | |
---|---|
If you don't use |
Use the initializer start_in_dir
to set the work directory:
execute( run_exe("test.exe"), start_in_dir("../foo") );
start_in_dir
also supports boost::filesystem::path
.
For portability reasons you want to use an absolute path with run_exe
if you set
the work directory with start_in_dir
:
boost::filesystem::path exe = "test.exe"; execute( run_exe(boost::filesystem::absolute(exe)), start_in_dir("../foo") );
On Windows a relative path is relative to the work directory of the parent
process. On POSIX a relative path is relative to the work directory set with
start_in_dir
as the directory is changed before the program starts.
Tip | |
---|---|
Use an absolute path with |
Boost.Process provides the initializer inherit_env
to inherit environment variables:
execute( run_exe("test.exe"), inherit_env() );
While inherit_env
is required on POSIX, environment variables are also inherited without this
initializer on Windows as on Windows environment variables are inherited
by default.
If you want to set environment variables for the child process explicitly,
use the initializer set_env
.
Boost.Process provides the initializers bind_stdin
,
bind_stdout
and bind_stderr
to setup standard streams. They are based on the classes boost::iostreams::file_descriptor_source
and boost::iostreams::file_descriptor_sink
from Boost.Iostreams:
file_descriptor_sink sink("stdout.txt"); execute( run_exe("test.exe"), bind_stdout(sink) );
In the example above the standard output stream of the child process is bound
to a file. All data written by the child process to the standard output stream
is written to stdout.txt. Have a look at
the Boost.Iostreams documentation
to find out what other devices you can use with boost::iostreams::file_descriptor_source
and boost::iostreams::file_descriptor_sink
.
Note | |
---|---|
If you don't bind a stream with one of the initializers it depends on the platform where the streams are bound to. On Windows the streams are bound to the console by default. On POSIX the streams are inherited from the parent process and are bound to whatever they are bound to in the parent process. |
If you want to close streams for a child process, use close_stdin
,
close_stdout
and close_stderr
:
execute( run_exe("test.exe"), bind_stdout(sink), close_stdin(), close_stderr() );
For POSIX Boost.Process also provides close_fd
to close a single file descriptor, close_fds
to close multiple file descriptors and close_fds_if
to close all file descriptors a predicate returns true
for.
Boost.Process provides the function create_pipe
to create an anonymous pipe. A parent and child process can use the pipe
to send and receive data:
boost::process::pipe p = create_pipe(); file_descriptor_sink sink(p.sink, close_handle); execute( run_exe("test.exe"), bind_stdout(sink) ); file_descriptor_source source(p.source, close_handle); stream<file_descriptor_source> is(source); std::string s; std::getline(is, s);
In the example above the standard output stream of the child process is bound
to the write-end of the pipe. The read-end is used by the parent process
to receive data. The class boost::iostreams::stream
is another class provided by Boost.Iostreams to wrap file descriptor sources
or sinks.
Note | |
---|---|
There is a Boost.Iostreams
bug on Windows in all versions up to 1.50.0. If you read from a
|
With the help of Boost.Asio
it's possible to send and receive data between parent and child processes
asynchronously. On Windows a named pipe must be used though as Windows doesn't
support asynchronous I/O with anonymous pipes. That's why create_pipe
can't be used for asynchronous I/O - at least not on Windows. The following
example expects that a function called create_async_pipe
has been defined which returns a pipe on all platforms supporting asynchronous
I/O:
boost::process::pipe p = create_async_pipe(); file_descriptor_sink sink(p.sink, close_handle); execute( run_exe("test.exe"), bind_stdout(sink) ); file_descriptor_source source(p.source, close_handle); #if defined(BOOST_WINDOWS_API) typedef boost::asio::windows::stream_handle pipe_end; #elif defined(BOOST_POSIX_API) typedef boost::asio::posix::stream_descriptor pipe_end; #endif boost::asio::io_service io_service; pipe_end pend(io_service, p.source); boost::array<char, 4096> buffer; boost::asio::async_read(pend, boost::asio::buffer(buffer), [](const boost::system::error_code&, std::size_t){}); io_service.run();
For asynchronous I/O Boost.Asio must be used. Boost.Asio provides the I/O
objects boost::asio::windows::stream_handle
on Windows and boost::asio::posix::stream_descriptor
on POSIX which can be initialized with a read- or write-end of a pipe. Once
the I/O objects have been created it's possible to use the asynchronous operations
Boost.Asio provides.
Note | |
---|---|
|
Note | |
---|---|
There is a Boost.Iostreams
bug on Windows in all versions up to 1.50.0. If you read from a
|
Note | |
---|---|
Please note that |
Call wait_for_exit
to wait for a program to exit:
child c = execute(run_exe("test.exe")); auto exit_code = wait_for_exit(c);
On Windows wait_for_exit
returns the exit code of the program as a DWORD
.
On POSIX the function returns the status of the program as an int
. DWORD
is defined as unsigned long
.
While both are numeric integer types, you must use the macro WEXITSTATUS
to get the exit code
on POSIX.
Note | |
---|---|
|
wait_for_exit
is a blocking function. If you want to wait asynchronously, use Boost.Process
together with Boost.Asio:
boost::asio::io_service io_service; #if defined(BOOST_POSIX_API) int status; boost::asio::signal_set set(io_service, SIGCHLD); set.async_wait( [&status](const boost::system::error_code&, int) { ::wait(&status); } ); #endif child c = execute( run_exe("test.exe") #if defined(BOOST_POSIX_API) , notify_io_service(io_service) #endif ); #if defined(BOOST_WINDOWS_API) DWORD exit_code; boost::asio::windows::object_handle handle(io_service, c.process_handle()); handle.async_wait( [&handle, &exit_code](const boost::system::error_code&) { ::GetExitCodeProcess(handle.native(), &exit_code); } ); #endif io_service.run();
Call terminate
to close a program:
child c = execute(run_exe("test.exe")); terminate(c);
terminate
closes
a program immediately and forcefully. It is a last resort function as it
doesn't give the program to be closed any chance to clean up resources. For
instance, if the program is in the middle of writing data to a file, terminate
can leave that data
in an undefined state.