如何在使用POSIX的C++中执行命令并获取命令输出?

592

我正在寻找一种方法,在C++程序内运行命令并获取其输出。我已经尝试使用system()函数,但那只会执行一个命令。以下是我想要的示例:

std::string result = system("./some_command");

我需要运行任意命令并获取其输出。我查看了boost.org,但是我没有找到能够满足我的需求的内容。


3
请参考以下问题的答案:https://dev59.com/6lQK5IYBdhLWcg3wQdvI。该问题的回答扩展了下面这个很好的答案,提供了获取return codestderr和本回答已经解释的stdout的方法。请注意,不要更改原始意思,并尽可能使翻译通俗易懂。 - code_fodder
8
@code_fodder,您可以创建一个指向https://dev59.com/6lQK5IYBdhLWcg3wQdvI的链接。 - Jonas Stein
1
以下是5个关于C和/或C++的问题和答案,似乎涉及到这个主题:1)如何在C中从stdout读取,2)C:运行系统命令并获取输出?,3)如何从C运行外部程序并解析其输出?,4)最优地捕获system()命令的stdout,5)(本问题)。 - Gabriel Staples
12个回答

781
#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;
}

早期的C++11版本:

#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;
}

popenpclose 替换为 Windows 下的 _popen_pclose


98
请注意,这将仅获取标准输出(stdout),而不是标准错误(stderr)。 - kalaxy
16
请注意,result += buffer 可能会导致异常抛出,因此管道可能无法正确关闭。 - Fred Foo
11
答案不错,但如果将'char* cmd'替换为'const char* cmd'会更好。 - fnc12
33
unique_ptr更适合这里,因为实际的引用计数从未被使用。 - Czipperz
15
这在C++17中仍然是最佳实践吗? - Aaron Franke
显示剩余33条评论

101

使用我的pstreams头文件可以轻松获取标准输出和标准错误输出(这里不显示写入标准输入)。该头文件定义了类似于popen的iostream类:

#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';
} 

32
我不同意。使用popen需要使用C标准I/O API,而我更喜欢iostreams API。popen需要手动清理FILE句柄,而pstreams会自动完成。popen只接受一个const char*参数,这需要非常小心以避免Shell注入攻击,而pstreams允许您传递类似于execv的字符串向量,这更安全。popen只提供了一个管道,而pstreams则告诉您子进程的PID,从而允许您发送信号,例如如果它被阻塞或没有退出,则可以将其杀死。即使您只想要单向IO,所有这些都是优点。 - Jonathan Wakely
1
这种解决方案的另一个问题是,如果子进程写入stderr足以填满缓冲区并在开始写入stdout之前被阻塞。 父进程将阻塞读取stdout,而子进程则会等待stderr被读取时发生资源死锁!那么,至少其中一个循环最好是异步的(即,线程化)。 - Jesse Chisholm
1
@JesseChisholm,是的,那可能会成为一个问题。但是你不需要使用线程,因为pstreams允许使用iostream接口的近似非阻塞I/O,具体来说是使用readsome函数,该函数使用pstreambuf::in_avail()检查就绪状态,因此不会阻塞。这允许在进程的stdout和stderr上进行多路复用,因为每个都有可用数据。如果操作系统支持非标准的FIONREAD ioctl,则pstreambuf::in_avail()仅能100%可靠地工作,但是至少在GNU / Linux和Solaris上支持它。 - Jonathan Wakely
13
新的 1.0.1 版本采用了 Boost 许可证。 - Jonathan Wakely
1
@JonathanWakely,我该如何在5秒超时后终止ipstream? - A. K.
显示剩余13条评论

46
对于Windows系统,popen同样可行,但它会打开一个控制台窗口——迅速地闪过你的UI应用程序。如果您想成为一名专业人士,最好禁用这种"闪烁"操作(特别是如果最终用户可以取消它)。
因此,这里是我自己的版本(该代码部分由The Code Project和MSDN示例中的想法重新组合而来)。
#include <windows.h>
#include <atlstr.h>
//
// Execute a command and get the results. (Only standard output)
//
CStringA ExecCmd(
    const wchar_t* cmd              // [in] command to execute
)
{
    CStringA strResult;
    HANDLE hPipeRead, hPipeWrite;

    SECURITY_ATTRIBUTES saAttr = {sizeof(SECURITY_ATTRIBUTES)};
    saAttr.bInheritHandle = TRUE; // Pipe handles are inherited by child process.
    saAttr.lpSecurityDescriptor = NULL;

    // Create a pipe to get results from child's stdout.
    if (!CreatePipe(&hPipeRead, &hPipeWrite, &saAttr, 0))
        return strResult;

    STARTUPINFOW si = {sizeof(STARTUPINFOW)};
    si.dwFlags     = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
    si.hStdOutput  = hPipeWrite;
    si.hStdError   = hPipeWrite;
    si.wShowWindow = SW_HIDE; // Prevents cmd window from flashing.
                              // Requires STARTF_USESHOWWINDOW in dwFlags.

    PROCESS_INFORMATION pi = { 0 };

    BOOL fSuccess = CreateProcessW(NULL, (LPWSTR)cmd, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
    if (! fSuccess)
    {
        CloseHandle(hPipeWrite);
        CloseHandle(hPipeRead);
        return strResult;
    }

    bool bProcessEnded = false;
    for (; !bProcessEnded ;)
    {
        // Give some timeslice (50 ms), so we won't waste 100% CPU.
        bProcessEnded = WaitForSingleObject( pi.hProcess, 50) == WAIT_OBJECT_0;

        // Even if process exited - we continue reading, if
        // there is some data available over pipe.
        for (;;)
        {
            char buf[1024];
            DWORD dwRead = 0;
            DWORD dwAvail = 0;

            if (!::PeekNamedPipe(hPipeRead, NULL, 0, NULL, &dwAvail, NULL))
                break;

            if (!dwAvail) // No data available, return
                break;

            if (!::ReadFile(hPipeRead, buf, min(sizeof(buf) - 1, dwAvail), &dwRead, NULL) || !dwRead)
                // Error, the child process might ended
                break;

            buf[dwRead] = 0;
            strResult += buf;
        }
    } //for

    CloseHandle(hPipeWrite);
    CloseHandle(hPipeRead);
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    return strResult;
} //ExecCmd

1
这是我最喜欢的Windows解决方案,希望你能原谅我的更改。我建议使const-cast更明确,而我认为显式使用wchar_tCreateProcessW是不必要的限制。 - Wolf
你是否认为这个类型转换存在问题或潜在问题? 我更喜欢保持代码的最小化,并且不会毫无必要地编写它。 - TarmoPikaro
6
阅读完CreateProcess函数(Windows)后,我发现这样做存在真正的危险:“此函数的 Unicode 版本 CreateProcessW 可以修改该字符串的内容。因此,此参数不能是指向只读内存(例如 const 变量或文字常量)的指针。如果此参数是常量字符串,则该函数可能会导致访问冲突。” 所以最好将命令行先复制到一个单独的缓冲区中,以防止调用者更改其原始输入。 - Wolf
1
此答案未正确处理标准错误(stderr)。 - Refael Sheinker
这对Unix系统也适用吗?还是我需要为Unix设备使用其他东西? - 255.tar.xz

39

我会使用 popen() 函数 (++waqas)

但有时候您需要进行读写操作...

看起来似乎没人再用麻烦的方式了。

(假设在Unix/Linux/Mac环境下,或者在Windows上使用POSIX兼容层...)

enum PIPE_FILE_DESCRIPTERS
{
  READ_FD  = 0,
  WRITE_FD = 1
};

enum CONSTANTS
{
  BUFFER_SIZE = 100
};

int
main()
{
  int       parentToChild[2];
  int       childToParent[2];
  pid_t     pid;
  string    dataReadFromChild;
  char      buffer[BUFFER_SIZE + 1];
  ssize_t   readResult;
  int       status;

  ASSERT_IS(0, pipe(parentToChild));
  ASSERT_IS(0, pipe(childToParent));

  switch (pid = fork())
  {
    case -1:
      FAIL("Fork failed");
      exit(-1);

    case 0: /* Child */
      ASSERT_NOT(-1, dup2(parentToChild[READ_FD], STDIN_FILENO));
      ASSERT_NOT(-1, dup2(childToParent[WRITE_FD], STDOUT_FILENO));
      ASSERT_NOT(-1, dup2(childToParent[WRITE_FD], STDERR_FILENO));
      ASSERT_IS(0, close(parentToChild [WRITE_FD]));
      ASSERT_IS(0, close(childToParent [READ_FD]));

      /*     file, arg0, arg1,  arg2 */
      execlp("ls", "ls", "-al", "--color");

      FAIL("This line should never be reached!!!");
      exit(-1);

    default: /* Parent */
      cout << "Child " << pid << " process running..." << endl;

      ASSERT_IS(0, close(parentToChild [READ_FD]));
      ASSERT_IS(0, close(childToParent [WRITE_FD]));

      while (true)
      {
        switch (readResult = read(childToParent[READ_FD],
                                  buffer, BUFFER_SIZE))
        {
          case 0: /* End-of-File, or non-blocking read. */
            cout << "End of file reached..."         << endl
                 << "Data received was ("
                 << dataReadFromChild.size() << "): " << endl
                 << dataReadFromChild                << endl;

            ASSERT_IS(pid, waitpid(pid, & status, 0));

            cout << endl
                 << "Child exit staus is:  " << WEXITSTATUS(status) << endl
                 << endl;

            exit(0);


          case -1:
            if ((errno == EINTR) || (errno == EAGAIN))
            {
              errno = 0;
              break;
            }
            else
            {
              FAIL("read() failed");
              exit(-1);
            }

          default:
            dataReadFromChild . append(buffer, readResult);
            break;
        }
      } /* while (true) */
  } /* switch (pid = fork())*/
}

你可能还想尝试使用select()和非阻塞读取。

fd_set          readfds;
struct timeval  timeout;

timeout.tv_sec  = 0;    /* Seconds */
timeout.tv_usec = 1000; /* Microseconds */

FD_ZERO(&readfds);
FD_SET(childToParent[READ_FD], &readfds);

switch (select (1 + childToParent[READ_FD], &readfds, (fd_set*)NULL, (fd_set*)NULL, & timeout))
{
  case 0: /* Timeout expired */
    break;

  case -1:
    if ((errno == EINTR) || (errno == EAGAIN))
    {
      errno = 0;
      break;
    }
    else
    {
      FAIL("Select() Failed");
      exit(-1);
    }

  case 1:  /* We have input */
    readResult = read(childToParent[READ_FD], buffer, BUFFER_SIZE);
    // However you want to handle it...
    break;

  default:
    FAIL("How did we see input on more than one file descriptor?");
    exit(-1);
}

2
“走弯路是正确的 :) 我喜欢使用select()调用的想法,但在这种情况下,我实际上需要等待任务完成。我会把这段代码留给我另一个项目使用 :)” - Misha M
4
...或者您可以使用现有的posix_spawnp函数。 - Per Johansson
7
你的execlp调用有一个错误:必须将最后传递的arg指针设置为(char *) NULL,以正确终止可变参数列表(请参见execlp(3)进行参考)。 - Kristóf Marussy
1
这个能在Unix、Linux和Windows上运行吗?请问您能提供头文件吗? - kittu

21

有两种可能的方法:

  1. 我认为 popen() 不是C++标准的一部分(如果我没记错,它是来自POSIX),但它在我使用过的每个UNIX系统上都可用(而且你似乎正在针对UNIX,因为你的命令是 ./some_command)。

  2. 如果没有 popen() 的话,您可以使用 system("./some_command >/tmp/some_command.out");,然后使用常规的I/O函数处理输出文件。


13

我无法理解为什么Code::Blocks/MinGW缺少popen/pclose,所以我通过使用CreateProcess()CreatePipe()来解决这个问题。

这是对我有效的解决方案:

//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());
                ListStdErr += 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;
}

6
谢谢!这是互联网上最好的Windows popen实现!通过传递CREATE_NO_WINDOW标志,最终可以摆脱烦人的cmd提示框。 - LachoTomov
1
你在哪里传递 CREATE_NO_WINDOW 这个东西? - Refael Sheinker
3
如果您注意到,@Bill Moore 的回答中有一个错误。ListStdErr 从未被使用过。 - Refael Sheinker
1
@RefaelSheinker:我认为你可以在createProcess中用CREATE_NO_WINDOW替换0。https://learn.microsoft.com/en-us/windows/win32/procthread/process-creation-flags - Eric Duminil
1
@RefaelSheinker:确实。如果您想要两个不同的字符串,我认为第二个ListStdOut += s; 应该被替换为 ListStdErr += s;。不过我希望将它们合并,所以我会简单地删除ListStdErr。最后,List这个名称对于一个字符串来说有点奇怪。 - Eric Duminil

12
以下可能是一种可移植的解决方案。它遵循标准。
#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
#include <sstream>

std::string ssystem (const char *command) {
    char tmpname [L_tmpnam];
    std::tmpnam ( tmpname );
    std::string scommand = command;
    std::string cmd = scommand + " >> " + tmpname;
    std::system(cmd.c_str());
    std::ifstream file(tmpname, std::ios::in | std::ios::binary );
    std::string result;
    if (file) {
        while (!file.eof()) result.push_back(file.get())
            ;
        file.close();
    }
    remove(tmpname);
    return result;
}

// For Cygwin

int main(int argc, char *argv[])
{
    std::string bash = "FILETWO=/cygdrive/c/*\nfor f in $FILETWO\ndo\necho \"$f\"\ndone ";
    std::string in;
    std::string s = ssystem(bash.c_str());
    std::istringstream iss(s);
    std::string line;
    while (std::getline(iss, line))
    {
        std::cout << "LINE-> " + line + "  length: " << line.length() << std::endl;
    }
    std::cin >> in;
    return 0;
}

4
我用gcc编译器会收到这个警告:"warning: the use of tmpnam is dangerous, better use mkstemp",意思是使用tmpnam函数存在风险,最好改用mkstemp函数。 - Mark Lakata

11

请注意,您可以通过将输出重定向到文件然后读取它来获取输出。

std::system的文档中已经说明。

您可以通过调用WEXITSTATUS宏来接收退出代码。

    int status = std::system("ls -l >test.txt"); // execute the UNIX command "ls -l >test.txt"
    std::cout << std::ifstream("test.txt").rdbuf();
    std::cout << "Exit code: " << WEXITSTATUS(status) << std::endl;

这只能部分地工作。如果你想要检索已启动进程的ID,它将无法工作。 - infinitezero

5

Command类使用system("cmd > stdout 2> stderr")提供用户标准输出和标准错误输出,同时还提供了退出代码。

测试运行:

./a.out 'ls .'
exit code: 0
stdout: HelloWorld
HelloWorld.c
HelloWorld.cpp
HelloWorld.dSYM
a.out
gcc_container.bash
linuxsys
macsys
test.sh

stderr: 


#include <iostream>
#include <fstream>
#include <sstream>
#include <unistd.h>
using namespace std;

class Command {
    public:
        Command() {
            exit_code_ = -1;
        }

        int GetExitCode() { return exit_code_;}

        string GetStdOutStr() {return stdout_str_;}

        string GetStdErrStr() {return stderr_str_;}

        int Run(const char* cmd) {
            return Run(string(cmd));
        }

        /**
         * @brief run a given command
         * 
         * @param cmd: command string
         * @return int: the exit code of running the command
         */
        int Run(string cmd) {

            // create temp files
            char tmp_dir[] = "/tmp/stdir.XXXXXX";
            mkdtemp(tmp_dir);
            string stdout_file = string(tmp_dir) + "/stdout";
            string stderr_file = string(tmp_dir) + "/stderr";

            // execute the command "cmd > stdout_file 2> stderr_file"
            string cli = cmd + " > " + stdout_file + " 2> " + stderr_file;
            exit_code_ = system(cli.c_str());
            exit_code_ = WEXITSTATUS(exit_code_);
            stdout_str_ = File2Str(stdout_file);
            stderr_str_ = File2Str(stderr_file);

            // rid of the temp files
            remove(stdout_file.c_str());
            remove(stderr_file.c_str());
            remove(tmp_dir);

            return exit_code_;
        }

    private:
        int exit_code_;
        string stderr_str_;
        string stdout_str_;

        /**
         * @brief read a file
         * 
         * @param file_name: file path 
         * @return string the contents of the file.
         */
        string File2Str(string file_name) {
            ifstream file;
            stringstream str_stream;

            file.open(file_name);
            if (file.is_open()) {
                str_stream << file.rdbuf();
                file.close();
            }
            return str_stream.str();
        }
};

int main(int argc, const char* argv[]) {
    Command command;

    command.Run(argv[1]);
    cout << "exit code: " << command.GetExitCode() << endl;
    cout << "stdout: " << command.GetStdOutStr() << endl;
    cout << "stderr: " << command.GetStdErrStr() << endl;
    return  command.GetExitCode();
}


5

假设使用POSIX,捕获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;
}

欢迎为更多功能做代码贡献: https://github.com/ericcurtin/execxx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接