Как выполнить команду и получить код возврата stdout и stderr команды в C ++ - PullRequest
0 голосов
/ 04 сентября 2018

Учитывая следующий ответ (первый ответ c ++ 11):

Как выполнить команду и получить вывод команды в C ++, используя POSIX?

Вот реализация для вашего удобства:

#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::shared_ptr<FILE> pipe(popen(cmd, "r"), pclose);
    if (!pipe) throw std::runtime_error("popen() failed!");
    while (!feof(pipe.get())) {
        if (fgets(buffer.data(), 128, pipe.get()) != nullptr)
            result += buffer.data();
    }
    return result;
}

Это очень хорошо работает для выполнения команды (например, std::string res = exec("ls");) и получения stdout в строку.

Но что он не делает, так это получает код возврата команды (pass / fail integer) или stderr. В идеале мне нужен способ получить все три (код возврата, стандартный вывод, стандартный вывод).

Я бы согласился на стандартный вывод и стандартный вывод. Я думаю, что мне нужно добавить еще один канал, но я не могу понять, как настроен первый канал, чтобы получить стандартный вывод, поэтому я не могу думать, как бы изменить его, чтобы получить оба.

У кого-нибудь есть идеи, как это сделать, или альтернативные подходы, которые могут работать?

обновление

Смотрите мой полный пример здесь с выводом:

Start
1 res: /home

2 res: stdout

stderr
3 res: 
End

Вы можете видеть, что 3 res: не печатает stderr так же, как 2 res: stdout, но stderr просто выводится на экран отдельной строкой процесса (а не моей программы).

Внешние библиотеки

Я действительно не хочу использовать внешние библиотеки, такие как Qt и boost - в основном потому, что мне нужна его переносимость, а также многие проекты, над которыми я работаю, не используют boost. Однако я отмечу решения, которые содержат эти параметры, так как они действительны для других пользователей:)

Полное решение с использованием комментариев / ответов

Спасибо всем за ваши ответы / комментарии, вот модифицированное решение (и работоспособное):

работоспособное решение

Ответы [ 3 ]

0 голосов
/ 04 сентября 2018

Возможен обходной путь. Вы можете перенаправить stderr в stdout, добавив «2> & 1» к вашему cmd. Подойдет ли это вам?

0 голосов
/ 18 февраля 2019

Вы можете получить код возврата из канала, используя пользовательское средство удаления как таковое:

#include <cstdio>
#include <iostream>
#include <memory>
#include <string>
#include <array>
#include <utility>

using namespace std;
pair<string, int> exec(const char* cmd) {
    array<char, 128> buffer;
    string result;
    int return_code = -1;
    auto pclose_wrapper = [&return_code](FILE* cmd){ return_code = pclose(cmd); };
    { // scope is important, have to make sure the ptr goes out of scope first
    const unique_ptr<FILE, decltype(pclose_wrapper)> pipe(popen(cmd, "r"), pclose_wrapper);
    if (pipe) {
        while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
            result += buffer.data();
        }
    }
    }
    return make_pair(result, return_code);
}

int main(int argc, char* argv[]) {
    if (argc <= 1) return 0;
    cout << "calling with " << argv[1] << '\n';
    const auto process_ret = exec(argv[1]);
    cout << "captured stdout : " << '\n' << process_ret.first << endl;
    cout << "program exited with status code " << process_ret.second << endl;
    return 0;
} 
0 голосов
/ 04 сентября 2018

С man-страницы popen:

The pclose() function waits for the associated process to terminate  and returns the exit status of the command as returned by wait4(2).

Таким образом, вызов pclose() самостоятельно (вместо использования магии деструктора std::shared_ptr<>) даст вам код возврата вашего процесса (или блока, если процесс не завершился).

std::string exec(const char* cmd) {
    std::array<char, 128> buffer;
    std::string result;

    auto pipe = popen(cmd, "r"); // get rid of shared_ptr

    if (!pipe) throw std::runtime_error("popen() failed!");

    while (!feof(pipe)) {
        if (fgets(buffer.data(), 128, pipe) != nullptr)
            result += buffer.data();
    }

    auto rc = pclose(pipe);

    if (rc == EXIT_SUCCESS) { // == 0

    } else if (rc == EXIT_FAILURE) {  // EXIT_FAILURE is not used by all programs, maybe needs some adaptation.

    }
    return result;
}

Получение stderr и stdout с помощью popen(), я боюсь, вам нужно перенаправить вывод stderr на stdout из командной строки, которую вы передаете popen (), добавив 2>&1. Это вызывает неудобство, так как оба потока непредсказуемо смешаны.

Если вы действительно хотите иметь два выделенных файловых дескриптора для stderr и stdout, один из способов сделать это - выполнить разветвление самостоятельно и дублировать новые процессы stdout / stderr на два канала, которые доступны из родительского процесса. (см. dup2() и pipe()). Я мог бы рассказать здесь подробнее, но это довольно утомительный способ сделать что-то, и нужно быть очень внимательным. А в интернете полно примеров.

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