UTL

Collection of self-contained header-only libraries for C++17

View on GitHub

utl::shell

<- to README.md

<- to implementation.hpp

utl::shell is a small header that contains:

It is mainly useful for invoking scripts and other executables in a portable (but not particularly secure) way.

Definitions

// Temporary files
struct TemporaryHandle {
    TemporaryHandle()                       = delete;
    TemporaryHandle(const TemporaryHandle&) = delete;
    TemporaryHandle(TemporaryHandle&&)      = default;
    
    // Construction
    static TemporaryHandle    create(std::filesystem::path filepath);
    static TemporaryHandle    create(                              );
	static TemporaryHandle overwrite(std::filesystem::path filepath);
    static TemporaryHandle overwrite(                              );
    
    // Getters
    std::ifstream ifstream(std::ios::openmode mode = std::ios::in ) const;
    std::ofstream ofstream(std::ios::openmode mode = std::ios::out) const;
    
    const std::filesystem::path& path() const noexcept;
    const std::string          &  str() const noexcept;
};

// Shell commands
struct CommandResult {
    int         status;
    std::string out;
    std::string err;
};

CommandResult run_command(const std::string& command);

Methods

Temporary files

TemporaryHandle()                       = delete;
TemporaryHandle(const TemporaryHandle&) = delete;
TemporaryHandle(TemporaryHandle&&)      = default;

TemporaryHandle “owns” the file lifetime and has move-only semantics.

static TemporaryHandle    create(std::filesystem::path filepath); // (1)
static TemporaryHandle    create(                              ); // (2)

Overload (1) generates temporary file with a given filepath.

Overload (2) generates file with a unique name inside the system temporary directory.

Does not overwrite existing files in case of a name collision, throws std::runtime_error if new file would replace an existing one.

static TemporaryHandle overwrite(std::filesystem::path filepath); // (1)
static TemporaryHandle overwrite(                              ); // (2)

Overload (1) generates temporary file with a given filepath.

Overload (2) generates file with a unique name inside the system temporary directory.

Overwrites existing files in case of a name collision.

std::ifstream ifstream(std::ios::openmode mode = std::ios::in ) const;
std::ofstream ofstream(std::ios::openmode mode = std::ios::out) const;

Returns std::ifstream / std::ofstream associated with the temporary file.

Throws std::runtime_error in case of an IO failure.

const std::filesystem::path& path() const noexcept;
const std::string          &  str() const noexcept;

Returns std::filesystem::path / std::string associated with the temporary file.

Shell commands

struct CommandResult {
    int         status;
    std::string out;
    std::string err;
};

CommandResult run_command(const std::string &command);

Runs command using a default system shell (cmd for Windows, bash for Linux).

Return status, stdout and stderr (see standard streams) piped from the shell.

Note: It is assumed that command does not redirect it’s own streams. In case stream redirection is necessary, command can usually be wrapped in a subshell, for example in bash command echo HELLO >&2 can be rewritten as(echo HELLO >&2) to add a subshell.

Examples

Working with temporary files

[ Run this code ]

const auto handle = utl::shell::TemporaryHandle::overwrite("temporary.txt");

// Write to temporary file
handle.ofstream() << "TEXT";

// Read from temporary file
std::string          text;
handle.ifstream() >> text;

assert(text == "TEXT");

// Append some more text
handle.ofstream(std::ios_base::app) << "MORE TEXT";

// Temp. file is deleted once handle is destroyed

Running shell commands

[!Warning] Online compiler explorer does not work with std::system, failing the command is expected.

[ Run this code ]

const auto res = utl::shell::run_command("echo TEXT");
// usually used to invoke scripts and other executables

assert(res.status ==      0);
assert(res.out    == "TEXT");
assert(res.err    ==     "");