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

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

我可以使用 fork、execvp、pipe 等函数分别获取 stdout 和 stderr,并将它们放入两个单独的 C++ 字符串中。我如何使用这一系列函数将 stdout 和 stderr 组合成一个组合字符串,就像 shell 一样,就好像我正在像“2>&1”一样重定向?下面的示例仅捕获标准输出:

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

最佳答案

只用一根 pipe 把两者都收集起来:

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);

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

关于c++ - 如何将 stderr 和 stdout 组合成单个 C++ 字符串?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53401095/

相关文章:

c++ - 为什么 nullptr 是核心语言的一部分,而 nullptr_t 是 STL 的一部分?

c++ - 如何使用模板处理多类型变量?

c++ - 解决由于类之间的循环依赖而导致的构建错误

c++ - 为什么包含 '\0' 和 '\t' 的字符串不能使用 operator == 与 "\0\t"进行比较?

c++ - OpenCV C++ 接口(interface)如何管理 ROI

c - 我想将字符串写入文件顶部。但它不起作用。(在c中)

c++ - 通过删除点链接和重复的斜线规范化 Unix 文件路径

linux - 主机名中的 ssl 错误

linux - 如果 shell 子包含 "exit command",则从 shell 父亲调用 2 shell 并且不退出

c++ - 这两种添加字符串的情况有什么区别?