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(std::string_view 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(std::string_view command);
Runs command using a default system shell (cmd for Windows, bash for Linux, zsh for MacOS and some Linux distros).
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 ] [ Open source file ]
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::app) << "MORE TEXT";
// Temp. file is deleted once handle is destroyed
Running shell commands¶
Warning
Online compiler explorer does not support std::system, failing the [ Run this code ] is expected.
[ Run this code ] [ Open source file ]
#ifdef __linux__
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 == "");
#endif