【问题标题】:How to combine stderr and stdout to single C++ string?如何将 stderr 和 stdout 组合成单个 C++ 字符串?
【发布时间】:2019-04-23 08:59:52
【问题描述】:

我可以使用 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"});
}

【问题讨论】:

  • 感谢编辑
  • 看起来你从来没有从标准错误管道中读取数据。如果你想组合 stdout+stderr,为什么不把一个管道复制到 1 和 2(stdout 和 stderr)?
  • if (!pid) {...} 没有正确处理fork() 失败的情况。

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


【解决方案1】:

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

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 写入的内容足够多,管道已填满,子进程可能会阻塞。

【讨论】:

    猜你喜欢
    • 2011-07-22
    • 2020-04-26
    • 2013-06-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多