Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
huangxiangyao committed Aug 28, 2023
1 parent 5d3f081 commit f793041
Show file tree
Hide file tree
Showing 11 changed files with 458 additions and 29 deletions.
45 changes: 18 additions & 27 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,32 +1,23 @@
# Prerequisites
*.d
# Ignore cmake generated directories and files.
build/
out/
/CMakeSettings.json

# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Visual Studio files
.vs
*.suo
*.user

# Precompiled Headers
*.gch
*.pch
# VSCode files
.vscode/
.cache/
cmake-variants.yaml

# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# macOS files
.DS_Store

# Fortran module files
*.mod
*.smod
# Idea files
.idea/

# Compiled Static libraries
*.lai
*.la
*.a
*.lib

# Executables
*.exe
*.out
*.app
# python
*.pyc
23 changes: 23 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
cmake_minimum_required(VERSION 3.19)
project(cmdout.sln VERSION 0.0)

#set(CMAKE_CXX_STANDARD 11)
#set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD 17)
#set(CMAKE_CXX_STANDARD 20)
#set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(NOT CYGWIN AND NOT MSYS AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL QNX)
set(CMAKE_CXX_EXTENSIONS OFF)
endif()

include(CMakeDependentOption)
include(GNUInstallDirs)

option(BUILD_DEMO "Builds the demo subproject" ON)
option(INSTALL_CMDOUT "Enable installation of cmdout. (Projects embedding cmdout may want to turn this OFF.)" ON)

add_subdirectory(cmdout)
if(BUILD_DEMO)
add_subdirectory(demo)
endif()
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2023 Myvas
Copyright (c) 2023 Myvas Foundation

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
47 changes: 46 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,47 @@
# cmdout
To execute a command and get its output: stdout, stderr, status (exit code)
To execute a shell command and get its output, including stdout, stderr, status (exit code).

There are 3 overloads:
```
cmdout system(const char *cmd);
cmdout system(const char *cmd, unsigned timeout_ms);
cmdout system(const char *cmd, std::chrono::milliseconds timeout_ms);
```
- All of them will return a result or a timeout error, both are wrapped in a `cmdout` object.
- Each of them will wait for the result to become available until specified timeout has elapsed.
- The default value of timeout is **10 seconds**, if the `timeout_ms` argument is 0 or unspecified.

### Todo list
- By now, it works well on Linux; but it should work on more platforms.

## Getting Started
### CMake file
```
FetchContent_Declare(
cmdout
GIT_REPOSITORY https://github.com/myvas/cmdout.git
GIT_TAG 0.0.1 # release-0.0.1
)
FetchContent_MakeAvailable(cmdout)
#target_link_libraries(<your_target> PRIVATE cmdout)
```
### Include the header file
```
#include <cmdout.hpp>
```

## Demo
### C++17 on GNU/Linux Debian 11 (bullseye)
```
#include <cmdout_ext.hpp>
#include <iostream>
int main() {
auto result22 = myvas::system("ls not-exist 2>&1");
std::cout << result22 << std::endl;
auto result42 = myvas::system("ls / -l", std::chrono::milliseconds(4));
std::cout << result42 << std::endl;
}
```
11 changes: 11 additions & 0 deletions cmdout/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
find_package(Threads)

add_library(cmdout)
target_include_directories(cmdout PUBLIC include)
target_sources(cmdout PRIVATE src/cmdout.cpp "src/system.cpp")
target_link_libraries(cmdout PRIVATE ${CMAKE_THREAD_LIBS_INIT})
#set_target_properties(cmdout PROPERTIES COMPILE_FLAGS "-pthread" LINK_FLAGS "-pthread")

if(INSTALL_CMDOUT)
install(TARGETS cmdout)
endif()
125 changes: 125 additions & 0 deletions cmdout/include/cmdout.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/**
* Copyright (c) 2023 Myvas Foundation
* SPDX-License-Identifier: MIT
*
* @file cmdout.hpp
* @brief `myvas::system()` is used to invoke a shell command and get its output, within timeout feature.
*
* @see {std::system()}
*/
#pragma once

#include <string>
#include <string_view>
#include <chrono>
#include <cstdlib>
#include <cerrno>

namespace myvas {

/**
* @brief Executor to execute a shell command and get its output.
*/
class cmdout
{
int status_;
std::string out_;
std::string cmd_;

public:
/**
* @brief Gets the exit code after `exec` returned.
*
* The value is one of the following:
* - If `cmd` is empty, then a nonzero value if a shell is available, or 0 if
* no shell is available.
* - If a child process in `cmd` could not be created, or its status could not
* be retrieved, the return value is -1 and `errno` is set to indicate the
* error.
* - If a shell could not be executed, then the return value is 127.
* - If all system call succeed, then the return value is the status of the
* last command in `cmd`.
*/
int status() const;

void status(int value);

/**
* @brief Gets the content of `stdout` (file descriptor 1).
*/
std::string_view out() const;

void out(std::string_view value);

/**
* @brief Gets the shell command line.
*/
std::string_view cmd() const;

void cmd(std::string_view value);

/**
* @brief Default to cmdout(ENOTSUP, "Not supported").
*/
cmdout();

cmdout(std::string_view cmd);

/**
* @brief Constructs an output.
*
* @param [in] status EXIT_SUCCESS, EXIT_FAILURE or errno
* @see `cerrno` and `cstdlib`.
*/
explicit cmdout(int status, std::string_view out, std::string_view cmd);

cmdout(const cmdout&);
cmdout& operator=(const cmdout&);
cmdout(cmdout&&);
cmdout& operator=(cmdout&&);

virtual ~cmdout();
};

/**
* @brief Execute a shell command specified in `cmd`, waiting for the result until specified timeout (10 seconds) has elapsed.
* NOTE: A shell command with a tail of `2>&1` will catch both stdout and stderr, otherwise stdout only.
*
* @param [in] cmd The `cmd` argument is a pointer to a null-terminated string
* containing a shell command.
*
* @return The return value is a `cmdout` object, in which wraps the status code
* and output string of the shell command.
*/
cmdout system(const char* cmd);

/**
* @brief Execute a shell command specified in `cmd`, waiting for the result until specified timeout has elapsed.
* NOTE: A shell command with a tail of `2>&1` will catch both stdout and
* stderr, otherwise stdout only.
*
* @param [in] cmd The `cmd` argument is a pointer to a null-terminated string
* containing a shell command.
*
* @param [in] timeout_ms timeout in milliseconds. (If zero, defaults to 10 seconds)
*
* @return The return value is a `cmdout` object, in which wraps the status code
* and output string of the shell command.
*/
cmdout system(const char* cmd, unsigned timeout_ms);

/**
* @brief Execute a shell command specified in `cmd`, waiting for the result until specified timeout has elapsed.
* NOTE: A shell command with a tail of `2>&1` will catch both stdout and stderr, otherwise stdout only.
*
* @param [in] cmd The `cmd` argument is a pointer to a null-terminated string
* containing a shell command.
*
* @param [in] timeout_ms timeout in milliseconds. (If zero, defaults to 10 seconds)
*
* @return The return value is a `cmdout` object, in which wraps the status code
* and output string of the shell command.
*/
cmdout system(const char* cmd, std::chrono::milliseconds timeout_ms);

} // namespace myvas
22 changes: 22 additions & 0 deletions cmdout/include/cmdout_ext.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Copyright (c) 2023 Myvas Foundation
* SPDX-License-Identifier: MIT
*
* @file cmdout_ext.hpp
* @brief Helpers for `cmdout`.
*/
#pragma once

#include "cmdout.hpp"

#include <ostream>

/**
* @brief Helper for printing a cmdout object.
*/
std::ostream& operator<<(std::ostream& os, const myvas::cmdout& value)
{
os << "cmdout {" << value.cmd() << "} return " << value.status()
<< " length " << value.out().length() << "\n" << value.out();
return os;
}
50 changes: 50 additions & 0 deletions cmdout/src/cmdout.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* Copyright (c) 2023 Myvas Foundation
* SPDX-License-Identifier: MIT
*
* @file cmdout.cpp
* @brief Implementation of class `cmdout`.
*/
#include "cmdout.hpp"

#include <array>
#include <cstdio>
#include <cstring>
#include <stdexcept>

namespace myvas {

int cmdout::status() const { return status_; }

void cmdout::status(int value) { status_ = value; }

std::string_view cmdout::out() const { return out_; }

void cmdout::out(std::string_view value) { out_ = value; }

std::string_view cmdout::cmd() const { return cmd_; }

void cmdout::cmd(std::string_view value) { cmd_ = value; }

cmdout::cmdout()
: status_(ENOTSUP), out_("Not supported"), cmd_("")
{
}

cmdout::cmdout(std::string_view cmd)
: status_(ENOTSUP), out_("Not supported"), cmd_(cmd)
{
}

cmdout::cmdout(int status, std::string_view out, std::string_view cmd)
: status_(status), out_(out), cmd_(cmd)
{
}

cmdout::cmdout(const cmdout&) = default;
cmdout& cmdout::operator=(const cmdout&) = default;
cmdout::cmdout(cmdout&&) = default;
cmdout& cmdout::operator=(cmdout&&) = default;
cmdout::~cmdout() = default;

} // namespace myvas
Loading

0 comments on commit f793041

Please sign in to comment.