subprocess

Image Image Image Image Image Image

Image

A no nonsense library for writing shell commands in C++.

Writing shell commands in modern C++ is deceptively hard. There are many C++ subprocessing libraries out there, but none of them just work. The aim of this library is to allow you to write shell commands in C++ almost as if you were writing them in shell.

Full documentation for subprocess is available here.

Note: Windows is not currently supported.

TL;DR

It works exactly how you would expect it to work. Drop subprocess.hpp in your project, include it, and start working!

#include <subprocess.hpp>

using namespace subprocess::literals;

int main()
{
    std::string cmd_output;
    ("ls -l"_cmd | "awk 'NR>1{print $3}'"_cmd | "sort"_cmd | "uniq"_cmd > cmd_output).run();
    // Use cmd_output in the program
}

Philosophy

Instead of trying to emulate the subprocess interface from libraries in other languages, this library aims to use (and abuse) C++ operator overloading to achieve a shell-like syntax for writing shell commands.

subprocess follows these design goals:

  • Intuitive Syntax: Common operations on subprocess commands should feel like shell. This should allow users to compose commands in a familiar manner. There should ideally be no gotchas or differences in the behavior of subprocess and unix shell. In case such differences arise, they should be clearly documented.
  • Trivial Integration: The whole code comprises of a single subprocess.hpp and requires no adjustments to compiler flags or project settings. It has no dependencies, subprojects or dependencies on any build system.
  • Serious Testing: A CI pipeline performs heavy integration-testing that covers more than 90% of the code. These tests are run on all the platforms the library supports. Additionally, address and memory sanitizers are run to detect any memory or resourse leaks.

Overview

You can write shell commands using the subprocess::command class and use the resulting object to pipe I/O from/to standard streams, files, and variables.

Examples:

// Running a command
subprocess::command cmd{"touch" + file_path}.run();

// Piping the output of a command to another command
cmd | "uniq"_cmd;

Redirecting stdin

You can use operator< to redirect stdin to the command object.

Examples:

// Reading input from a file
cmd < std::filesystem::path{file_name};
cmd < "file_name";

// Reading input from a variable
std::string input{"abc"};
cmd < input;

// Reading from an already created fd
cmd < subprocess::file_descriptor{fd_no};

Redirecting stdout

The following operators are available for redirecting stdout:

  • operator>: Truncates the file and writes output
  • operator>>: Appends output to file if it already exists, otherwise creates one.

Examples:

// Redirecting stdout to stderr
cmd > subprocess::err;

// Redirecting stdout to a file
cmd > std::filesystem::path{file_name};

// or appending to a file
cmd >> std::filesystem::path{file_name};

// Capturing stdout in a variable
std::string var_name;
cmd > var_name;

Redirecting stderr

The following operators are available for redirecting stdout:

  • operator>=: Truncates the file and writes stderr
  • operator>>=: Appends stderr to file if it already exists, otherwise creates one.

Examples:

// Redirecting stderr to stdout
cmd >= subprocess::out;

// Redirecting stderr to a file
cmd >= std::filesystem::path{file_name};

// or appending to a file
cmd >>= std::filesystem::path{file_name};

// Capturing stderr in a variable
std::string var_name;
cmd >= var_name;

Conclusion

I would love your feedback! If you find any issues, feel free to log them.