subprocess: subprocess (original) (raw)
cross platform subprocess library for c++ similar to design of python subprocess. See subprocess documentation for further documentation.
supports
- very python like style of subprocess. With very nice syntax for c++20.
- Connect output of process A to input of process B. However not pretty API for this.
- Environment utilities to make it easy to get/set environment variables. as easy as
[subprocess::cenv](namespacesubprocess.html#aca240ebce6584aaa3d9d3f49fd3a166c)["MY_VAR"] = "value". - subprocess::EnvGuard that will save the environment and reload it when scope block ends, making it easy to have a temporary environment. Obviously this is not thread safe as environment variable changes effects process wide.
- Get a copy of environment so you can modify a std::map as you please for use in a thread safe manner of environment and pass it along to subprocesses.
- cross-platform
find_program - find_program has special handling of "python3" on windows making it easy to find python3 executable. It searches the path for python and inspects it's version so that
find_program("python3")is cross-platform. - Supports connecting process stdin, stdout, stderr to C++ streams making redirection convenient. stdin can be connected with a std::string too.
Shakey elements
- The os error level exceptions is still changing. I'm thinking of having an OSError subclass to abstract the OS differences.
requirements
- c++17
- linked with support for threading, filesystem
Integration
Adhoc
- copy files in src/cpp to your project.
- add the top folder as include.
- make sure cpp files are compiled.
- add
#include <[subprocess.hpp](subprocess%5F8hpp.html)>to start using in source files.
Teaport
add this to your dependencies:
Todo add to cocoapods and perhaps others.
Examples
#include
#include
void simple() {
CompletedProcess process = subprocess::run({"echo", "hello", "world"},
RunBuilder().cout(PipeOption::pipe));
RunBuilder().cin("hello world\n"));
RunBuilder().cin("hello world").cout(PipeOption::pipe));
std::cout << "captured: " << process.cout << '\n';
RunBuilder().cerr(PipeOption::pipe)
.cout(PipeOption::pipe)
.check(true)
);
std::cout << "cerr was: " << process.cerr << "\n";
#if __cplusplus >= 202002L
.cout = PipeOption::pipe,
.check = false
});
std::cout << "captured: " << process.cout << '\n';
#endif
}
void popen_examples() {
.cout(PipeOption::pipe).popen();
char buf[1024] = {0};
std::cout << buf;
popen.close();
std::thread write_thread([&]() {
popen.close_cin();
});
for (auto& c : buf)
c = 0;
std::cout << buf;
popen.close();
if (write_thread.joinable())
write_thread.join();
}
int main(int argc, char** argv) {
std::cout << "running basic examples\n";
simple();
std::cout << "running popen_examples\n";
popen_examples();
return 0;
}
ssize_t pipe_read(PipeHandle, void *buffer, size_t size)
PipeOption
Definition basic_types.hpp:103
@ cout
Redirects to stdout.
CompletedProcess run(Popen &popen, bool check=false)
ssize_t pipe_write(PipeHandle, const void *buffer, size_t size)
Definition basic_types.hpp:164
std::string cerr
Definition basic_types.hpp:174
std::string cout
Definition basic_types.hpp:172
Definition ProcessBuilder.hpp:91
Definition ProcessBuilder.hpp:282
Popen popen()
Definition ProcessBuilder.hpp:323
RunBuilder & cout(const PipeVar &cout)
Definition ProcessBuilder.hpp:296
Deviations
- On windows terminating a process sends CTRL_BREAK_EVENT instead of hard termination. You can send a SIGKILL and it will do a hard termination as expected. Becareful as this may kill your process as it's sent to the process group. See send_signal for more details.
- cin, cout, cerr variable names are used instead of stdin, stdout, stderr as std* are macros and cannot be used as names in C++.
current progress
All tests pass on linux & mac. Most pass under mingw & MSVC.
Changelog
0.5.0 2025-12-09
Breaking Changes
- pipe_create inheritable default is change to false. The subprocess API will automatically set inheritable as needed. In most cases it will fix bugs on your end at no additional effort. If you have an explicit reason to create inheritable pipes then you should evaluate your code as this change may introduce bugs into your code.
non-breaking Changes
- New pipe_set_blocking(), allows to change blocking mode.
- fixed #16 is_drive had a typo and so lowercase drives weren't properly interpretted. Thanks DarkCat5501
- fixed #2 subprocess.run() respects timeout passed in. Thanks wgshwn.
- breaking: RunOptions which is used in subprocess::run order is changed to be identical to python subprocess::run. This effects users using c++20 designated initializers. Prior versions of compilers didn't seem to care about order.
- Thanks to urs-muff for windows 64bit support
- Thanks GerHobbelt for the following
- fixed #5 cin double closed.
_DCRTIMPfor environ- order of check fixed, done so to match python ordering rather than changing example.
- yurivict Thanks for FREEBSD compatibility (I have no way of testing this) and use BUILD_TESTING in camke
- StableAgOH thanks for
- pointing out WC_ERR_INVALID_CHARS macro exists and std::string nullptr fix
- mingw compatibility fix
- github CI pipelines
- fixed: wait for background threads before destroying Popen.
0.4.0
CTRL_BREAK_EVENTis sent for SIGTERM & terminate() functions on windows.- fixed invalid handles when launching a python script that then launches new processes.
- new
kIsWin32constant to help avoid ifdef use. - Documentation wording to be more confident as the library is looking pretty good, and I haven't felt like changing much of the API.
0.3.0
- fixed MSVC issues & compiles
- documentation should be complete. Please report any missing
0.2.0
- omg setting check=true is fixed. What a typo