如何将stderr和stdout组合到单个C ++字符串?

时间:2018-11-20 20:34:29

标签: c++ linux c++11 unix posix

我可以使用fork,execvp,pipe等函数分别获取stdout和stderr并将它们放入两个单独的C ++字符串中。我如何使用这一系列函数将stdout和stderr都组合成一个单独的组合字符串(如shell),就像我要像“ 2>&1”一样进行重定向?下面的示例仅捕获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, NULL);
    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;
}

int main() {
  qx({"openssl", "hjas"});
  qx({"openssl", "dkjsah"});
  qx({"uname"});
  qx({"uname"});
}

1 个答案:

答案 0 :(得分:3)

只需使用一个管道收集两个管道:

std::string qx(const std::vector<std::string>& args) {
  int output[2];
  pipe(output);

  const pid_t pid = fork();
  if (!pid) {
    // collect both stdout and stderr to the one pipe
    close(output[0]);
    dup2(output[1], STDOUT_FILENO);
    dup2(output[1], STDERR_FILENO);
    close(output[1]);

    std::vector<char*> vc(args.size() + 1, NULL);
    for (size_t i = 0; i < args.size(); ++i) {
      vc[i] = const_cast<char*>(args[i].c_str());
    }

    execvp(vc[0], &vc[0]);
    // if execvp() fails, we do *not* want to call exit()
    // since that can call exit handlers and flush buffers
    // copied from the parent process
    _exit(0);
  }

  close(output[1]);

  std::string out;
  const int buf_size = 4096;
  char buffer[buf_size];
  do {
    errno = 0;
    const ssize_t r = read(output[0], buffer, buf_size);
    if (r > 0) {
      out.append(buffer, r);
    }
  } while (errno == EAGAIN || errno == EINTR);

请注意,您的原始代码不会收集孩子的stderr输出。如果子进程向stderr写入足够多的内容以使管道填满,则子进程可能会阻塞。