A modern, cross-platform, header-only C++ library for running and managing subprocesses. Inspired by Python's subprocess module and shell syntax, it aims to provide a simple, intuitive, and powerful API.
- Header-only: Simply include
include/subprocess/subprocess.hppin your project. - Cross-Platform: Works on Windows, Linux, and macOS.
- C++20 Standard: Leverages modern C++ features for a cleaner syntax.
- Easy to Use: The API is designed to be simple and intuitive, resembling shell commands.
- Powerful I/O Redirection:
- Easily redirect
stdin,stdout, andstderrto files, /dev/null, or in-memory buffers. - Supports
>(truncate) and>>(append) operators.
- Easily redirect
- Flexible Environment Variable Control:
- Completely replace environment variables for the child process.
- Add or modify variables on top of the existing environment.
- Capture Output: The
capture_runfunction makes it easy to capturestdoutandstderrfrom the subprocess. - Chainable Pipelines: Supports creating process pipelines like
cmd1 | cmd2(TODO). - Type-Safe: Utilizes the C++ type system to catch errors at compile time.
As a header-only library, the simplest way is to copy the include directory into your project and then include the header:
#include "subprocess/subprocess.hpp"You can add this repository as a submodule or download it directly into your project directory, then add the following to your CMakeLists.txt:
# Add subprocess.hpp to your project
add_subdirectory(path/to/subprocess.hpp)
# Link it to your target
target_link_libraries(your_target PRIVATE subprocess)If your project uses CMake 3.11 or later, you can use the FetchContent module to automatically download and integrate this library.
include(FetchContent)
FetchContent_Declare(
subprocess
GIT_REPOSITORY https://github.com/shediao/subprocess.hpp.git
GIT_TAG main # or a specific commit/tag
)
FetchContent_MakeAvailable(subprocess)
target_link_libraries(your_target PRIVATE subprocess)For convenience, it's recommended to import the following namespaces. All examples in this documentation assume this has been done.
#include <subprocess/subprocess.hpp>
// Core functions
using subprocess::run;
using subprocess::capture_run;
// Convenient alias, similar to shell
using subprocess::$;
// Named arguments
using namespace subprocess::named_arguments;Pass the command and its arguments directly. The run function returns the exit code of the subprocess.
// Equivalent to shell: ls -l /tmp
int exit_code = run("ls", "-l", "/tmp");
if (exit_code == 0) {
// success
} else {
// failed
}Use the capture_run function to easily capture the output of stdout and stderr. capture_run returns a tuple containing [exit_code, stdout_buffer, stderr_buffer].
auto [exit_code, out, err] = capture_run("echo", "Hello, Subprocess!");
if (exit_code == 0) {
std::cout << "Stdout: " << out.to_string() << std::endl;
}The API is designed to be as intuitive as the shell.
// Read stdin from a file
run("cat", $stdin < "input.txt");
// Redirect stdout to a file (truncate mode)
run("ls", "-l", $stdout > "output.txt");
// Redirect stderr to a file (append mode)
run("ls", "-l", "/non_existent_dir", $stderr >> "errors.log");
// Redirect all output to /dev/null (or NUL on Windows)
run("some_command", $stdout > $devnull, $stderr > $devnull);You can create process pipelines using subprocess::Pipe objects.
TODO:
Use the $cwd argument to specify the current working directory for the subprocess.
// Execute ls in the /tmp directory
auto [code, out, err] = capture_run("ls", $cwd = "/tmp");Use the $env argument to flexibly manage the subprocess's environment variables.
// Completely overwrite environment variables
run("printenv", $env = {{"MY_VAR", "123"}});
// Add/modify variables on top of the existing environment
run("printenv", $env += {{"NEW_VAR", "hello"}});
// Append a path to the PATH environment variable
// (Path separators are handled automatically on Windows)
run("my_program", $env["PATH"] += "/opt/my_app/bin");int run(...): Executes a command and waits for it to complete, returning the exit code. Arguments can be any combination of command parts, string arguments, and named arguments.int $(...): A convenient alias forrun.std::tuple<int, buffer, buffer> capture_run(...): Executes a command, waits for completion, and returns astd::tuplecontaining the exit code,stdoutbuffer, andstderrbuffer.
Named arguments are used to control various aspects of process execution.
-
$stdin < source: Redirect standard input fromsource.sourcecan be:std::string: A file path.subprocess::buffer: An in-memory buffer.subprocess::Pipe: Pipe output from another process.$devnull: The system's null device.
-
$stdout > target/$stderr > target: Redirect standard output/error totarget(truncate mode). -
$stdout >> target/$stderr >> target: Redirect standard output/error totarget(append mode).targetcan be:std::string: A file path.subprocess::buffer&: A reference to an in-memory buffer.subprocess::Pipe: For pipeline connections.$devnull: The system's null device.
-
$cwd = path: Execute the command in the specifiedpath(of typestd::stringorstd::wstring). -
$env = map: Completely replace the child process's environment variables with the key-value pairs inmap.maptype:std::map<std::string, std::string>.
-
$env += map: Add the key-value pairs frommapto the current environment. Existing variables will be overwritten. -
$env["VAR"] += value: Appendvalueto the end of the environment variable namedVAR(useful forPATH, etc.). -
$env["VAR"] <<= value: Prependvalueto the beginning of the environment variable namedVAR.
A simple buffer class for exchanging data with subprocesses.
buffer(): Creates an empty buffer.buffer(std::string_view): Creates a buffer from a string view.to_string(): Converts buffer content to astd::string.data(): Gets the rawchar*data.size(): Gets the buffer size.clear(): Clears the buffer.
The subprocess namespace also provides several useful cross-platform helper functions, which are aliased under the process namespace for convenience.
process::getenv(name): Gets an environment variable.process::environs(): Gets all environment variables.process::getcwd(): Gets the current working directory.process::chdir(path): Changes the current working directory.process::home(): Gets the user's home directory.process::pid(): Gets the current process ID.
Happy Coding!