Как объединить stderr и stdout в одну строку C ++? - PullRequest
0 голосов
/ 20 ноября 2018

Я могу получить stdout и stderr отдельно, используя функции fork, execvp, pipe и т. Д., И поместить их в две отдельные строки C ++.Как я могу использовать это семейство функций для объединения как stdout, так и stderr в одну объединенную строку, как если бы оболочка перенаправляла как «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"});
}

1 Ответ

0 голосов
/ 20 ноября 2018

Просто используйте одну трубу для сбора обоих:

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, чтобы канал заполнился, дочерний процесс может блокироваться.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...