utl::shell
utl::shell is a small header that contains:
- Temporary file creation with RAII handles
- A function to run shell commands while capturing status / stdout / stderr
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 == "");