How do I execute a command and get the output of the command within C++ using POSIX?

前端 未结 11 1153
我在风中等你
我在风中等你 2020-11-21 05:39

I am looking for a way to get the output of a command when it is run from within a C++ program. I have looked at using the system() function, but that will jus

相关标签:
11条回答
  • 2020-11-21 06:19

    I couldn't figure out why popen/pclose is missing from Code::Blocks/MinGW. So I worked around the problem by using CreateProcess() and CreatePipe() instead.

    Here's the solution that worked for me:

    //C++11
    #include <cstdio>
    #include <iostream>
    #include <windows.h>
    #include <cstdint>
    #include <deque>
    #include <string>
    #include <thread>
    
    using namespace std;
    
    int SystemCapture(
        string         CmdLine,    //Command Line
        string         CmdRunDir,  //set to '.' for current directory
        string&        ListStdOut, //Return List of StdOut
        string&        ListStdErr, //Return List of StdErr
        uint32_t&      RetCode)    //Return Exit Code
    {
        int                  Success;
        SECURITY_ATTRIBUTES  security_attributes;
        HANDLE               stdout_rd = INVALID_HANDLE_VALUE;
        HANDLE               stdout_wr = INVALID_HANDLE_VALUE;
        HANDLE               stderr_rd = INVALID_HANDLE_VALUE;
        HANDLE               stderr_wr = INVALID_HANDLE_VALUE;
        PROCESS_INFORMATION  process_info;
        STARTUPINFO          startup_info;
        thread               stdout_thread;
        thread               stderr_thread;
    
        security_attributes.nLength              = sizeof(SECURITY_ATTRIBUTES);
        security_attributes.bInheritHandle       = TRUE;
        security_attributes.lpSecurityDescriptor = nullptr;
    
        if (!CreatePipe(&stdout_rd, &stdout_wr, &security_attributes, 0) ||
                !SetHandleInformation(stdout_rd, HANDLE_FLAG_INHERIT, 0)) {
            return -1;
        }
    
        if (!CreatePipe(&stderr_rd, &stderr_wr, &security_attributes, 0) ||
                !SetHandleInformation(stderr_rd, HANDLE_FLAG_INHERIT, 0)) {
            if (stdout_rd != INVALID_HANDLE_VALUE) CloseHandle(stdout_rd);
            if (stdout_wr != INVALID_HANDLE_VALUE) CloseHandle(stdout_wr);
            return -2;
        }
    
        ZeroMemory(&process_info, sizeof(PROCESS_INFORMATION));
        ZeroMemory(&startup_info, sizeof(STARTUPINFO));
    
        startup_info.cb         = sizeof(STARTUPINFO);
        startup_info.hStdInput  = 0;
        startup_info.hStdOutput = stdout_wr;
        startup_info.hStdError  = stderr_wr;
    
        if(stdout_rd || stderr_rd)
            startup_info.dwFlags |= STARTF_USESTDHANDLES;
    
        // Make a copy because CreateProcess needs to modify string buffer
        char      CmdLineStr[MAX_PATH];
        strncpy(CmdLineStr, CmdLine.c_str(), MAX_PATH);
        CmdLineStr[MAX_PATH-1] = 0;
    
        Success = CreateProcess(
            nullptr,
            CmdLineStr,
            nullptr,
            nullptr,
            TRUE,
            0,
            nullptr,
            CmdRunDir.c_str(),
            &startup_info,
            &process_info
        );
        CloseHandle(stdout_wr);
        CloseHandle(stderr_wr);
    
        if(!Success) {
            CloseHandle(process_info.hProcess);
            CloseHandle(process_info.hThread);
            CloseHandle(stdout_rd);
            CloseHandle(stderr_rd);
            return -4;
        }
        else {
            CloseHandle(process_info.hThread);
        }
    
        if(stdout_rd) {
            stdout_thread=thread([&]() {
                DWORD  n;
                const size_t bufsize = 1000;
                char         buffer [bufsize];
                for(;;) {
                    n = 0;
                    int Success = ReadFile(
                        stdout_rd,
                        buffer,
                        (DWORD)bufsize,
                        &n,
                        nullptr
                    );
                    printf("STDERR: Success:%d n:%d\n", Success, (int)n);
                    if(!Success || n == 0)
                        break;
                    string s(buffer, n);
                    printf("STDOUT:(%s)\n", s.c_str());
                    ListStdOut += s;
                }
                printf("STDOUT:BREAK!\n");
            });
        }
    
        if(stderr_rd) {
            stderr_thread=thread([&]() {
                DWORD        n;
                const size_t bufsize = 1000;
                char         buffer [bufsize];
                for(;;) {
                    n = 0;
                    int Success = ReadFile(
                        stderr_rd,
                        buffer,
                        (DWORD)bufsize,
                        &n,
                        nullptr
                    );
                    printf("STDERR: Success:%d n:%d\n", Success, (int)n);
                    if(!Success || n == 0)
                        break;
                    string s(buffer, n);
                    printf("STDERR:(%s)\n", s.c_str());
                    ListStdOut += s;
                }
                printf("STDERR:BREAK!\n");
            });
        }
    
        WaitForSingleObject(process_info.hProcess,    INFINITE);
        if(!GetExitCodeProcess(process_info.hProcess, (DWORD*) &RetCode))
            RetCode = -1;
    
        CloseHandle(process_info.hProcess);
    
        if(stdout_thread.joinable())
            stdout_thread.join();
    
        if(stderr_thread.joinable())
            stderr_thread.join();
    
        CloseHandle(stdout_rd);
        CloseHandle(stderr_rd);
    
        return 0;
    }
    
    int main()
    {
        int            rc;
        uint32_t       RetCode;
        string         ListStdOut;
        string         ListStdErr;
    
        cout << "STARTING.\n";
    
        rc = SystemCapture(
            "C:\\Windows\\System32\\ipconfig.exe",    //Command Line
            ".",                                     //CmdRunDir
            ListStdOut,                              //Return List of StdOut
            ListStdErr,                              //Return List of StdErr
            RetCode                                  //Return Exit Code
        );
        if (rc < 0) {
            cout << "ERROR: SystemCapture\n";
        }
    
        cout << "STDOUT:\n";
        cout << ListStdOut;
    
        cout << "STDERR:\n";
        cout << ListStdErr;
    
        cout << "Finished.\n";
    
        cout << "Press Enter to Continue";
        cin.ignore();
    
        return 0;
    }
    
    0 讨论(0)
  • 2020-11-21 06:22
    #include <cstdio>
    #include <iostream>
    #include <memory>
    #include <stdexcept>
    #include <string>
    #include <array>
    
    std::string exec(const char* cmd) {
        std::array<char, 128> buffer;
        std::string result;
        std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
        if (!pipe) {
            throw std::runtime_error("popen() failed!");
        }
        while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
            result += buffer.data();
        }
        return result;
    }
    

    Pre-C++11 version:

    #include <iostream>
    #include <stdexcept>
    #include <stdio.h>
    #include <string>
    
    std::string exec(const char* cmd) {
        char buffer[128];
        std::string result = "";
        FILE* pipe = popen(cmd, "r");
        if (!pipe) throw std::runtime_error("popen() failed!");
        try {
            while (fgets(buffer, sizeof buffer, pipe) != NULL) {
                result += buffer;
            }
        } catch (...) {
            pclose(pipe);
            throw;
        }
        pclose(pipe);
        return result;
    }
    

    Replace popen and pclose with _popen and _pclose for Windows.

    0 讨论(0)
  • 2020-11-21 06:23

    Assuming POSIX, simple code to capture stdout:

    #include <sys/wait.h>
    #include <unistd.h>
    #include <string>
    #include <vector>
    
    std::string qx(const std::vector<std::string>& args) {
      int stdout_fds[2];
      pipe(stdout_fds);
    
      int stderr_fds[2];
      pipe(stderr_fds);
    
      const pid_t pid = fork();
      if (!pid) {
        close(stdout_fds[0]);
        dup2(stdout_fds[1], 1);
        close(stdout_fds[1]);
    
        close(stderr_fds[0]);
        dup2(stderr_fds[1], 2);
        close(stderr_fds[1]);
    
        std::vector<char*> vc(args.size() + 1, 0);
        for (size_t i = 0; i < args.size(); ++i) {
          vc[i] = const_cast<char*>(args[i].c_str());
        }
    
        execvp(vc[0], &vc[0]);
        exit(0);
      }
    
      close(stdout_fds[1]);
    
      std::string out;
      const int buf_size = 4096;
      char buffer[buf_size];
      do {
        const ssize_t r = read(stdout_fds[0], buffer, buf_size);
        if (r > 0) {
          out.append(buffer, r);
        }
      } while (errno == EAGAIN || errno == EINTR);
    
      close(stdout_fds[0]);
    
      close(stderr_fds[1]);
      close(stderr_fds[0]);
    
      int r, status;
      do {
        r = waitpid(pid, &status, 0);
      } while (r == -1 && errno == EINTR);
    
      return out;
    }
    

    Code contributions are welcome for more functionality:

    https://github.com/ericcurtin/execxx

    0 讨论(0)
  • 2020-11-21 06:24

    Getting both stdout and stderr (and also writing to stdin, not shown here) is easy peasy with my pstreams header, which defines iostream classes that work like popen:

    #include <pstream.h>
    #include <string>
    #include <iostream>
    
    int main()
    {
      // run a process and create a streambuf that reads its stdout and stderr
      redi::ipstream proc("./some_command", redi::pstreams::pstdout | redi::pstreams::pstderr);
      std::string line;
      // read child's stdout
      while (std::getline(proc.out(), line))
        std::cout << "stdout: " << line << '\n';
      # if reading stdout stopped at EOF then reset the state:
      if (proc.eof() && proc.fail())
        proc.clear();
      // read child's stderr
      while (std::getline(proc.err(), line))
        std::cout << "stderr: " << line << '\n';
    } 
    
    0 讨论(0)
  • 2020-11-21 06:26

    Two possible approaches:

    1. I don't think popen() is part of the C++ standard (it's part of POSIX from memory), but it's available on every UNIX I've worked with (and you seem to be targeting UNIX since your command is ./some_command).

    2. On the off-chance that there is no popen(), you can use system("./some_command >/tmp/some_command.out");, then use the normal I/O functions to process the output file.

    0 讨论(0)
提交回复
热议问题