выполнить командную строку из программы

Ответить
ya
^-^
Сообщения: 2675
Зарегистрирован: 16 дек 2021, 19:56

выполнить командную строку из программы

Сообщение ya »

Код: Выделить всё

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

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

    // Открываем поток для чтения результата выполнения команды
    std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
    if (!pipe) {
        throw std::runtime_error("popen() не удалось открыть поток");
    }

    // Читаем вывод команды по частям
    while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
        result += buffer.data();
    }

    return result;
}

int main() {
    // Команда для запуска другого приложения, например, ./myapp
    std::string output = exec("./myapp");
    std::cout << "Вывод другого приложения:\n" << output << std::endl;
    return 0;
}
Если result не вернула ожидаемых данных, возможно она выполнилась с ошибкой
ya
^-^
Сообщения: 2675
Зарегистрирован: 16 дек 2021, 19:56

Re: выполнить командную строку из программы

Сообщение ya »

Чтобы определить, завершилась ли вызываемая программа аварийно или успешно, можно использовать возвращаемый код завершения процесса. В popen() нельзя напрямую получить код завершения, но можно использовать pclose() для получения кода возврата.

Рассмотрим пример:

Код: Выделить всё

#include <iostream>
#include <cstdio>
#include <memory>
#include <stdexcept>
#include <string>
#include <array>
#include <cerrno>

int exec_and_check(const char* cmd, std::string& output) {
    std::array<char, 128> buffer;
    output.clear();

    // Открываем поток
    std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
    if (!pipe) {
        throw std::runtime_error("popen() не удалось открыть поток");
    }

    // Читаем вывод
    while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
        output += buffer.data();
    }

    // Получаем код завершения процесса
    int return_code = pclose(pipe.get());
    if (return_code == -1) {
        // Ошибка вызова pclose()
        throw std::runtime_error("Ошибка при вызове pclose()");
    }

    // В Linux/Unix система, если программа завершилась аварийно,
    // код завершения обычно отличается от 0.
    // В POSIX, код возврата процесса в диапазоне 0-255.
    // Если программа завершилась аварийно из-за сигнала, то return_code обычно
    // содержит информацию о сигнале (например, сигнал 9 или 11).
    // Можно проверить так:
    if (WIFSIGNALED(return_code)) {
        int sig = WTERMSIG(return_code);
        std::cerr << "Программа завершилась аварийно из-за сигнала: " << sig << std::endl;
        return sig; // Возвращает номер сигнала
    } else if (WIFEXITED(return_code)) {
        int exit_status = WEXITSTATUS(return_code);
        if (exit_status != 0) {
            std::cerr << "Программа завершилась с ошибкой, код: " << exit_status << std::endl;
        }
        return exit_status;
    } else {
        // Другие случаи
        return -1;
    }
}

int main() {
    std::string output;
    int result = exec_and_check("./myapp", output);
    std::cout << "Вывод:\n" << output << std::endl;
    if (result == 0) {
        std::cout << "Программа завершилась успешно.\n";
    } else if (result > 0) {
        std::cout << "Программа завершилась с ошибкой, код: " << result << std::endl;
    } else {
        std::cout << "Программа завершилась аварийно, сигнал: " << result << std::endl;
    }
    return 0;
}
Обратите внимание:

pclose() возвращает статус, который нужно интерпретировать с помощью WIFEXITED(), WEXITSTATUS(), WIFSIGNALED(), WTERMSIG().
Для этого нужно подключить <sys/wait.h> (в Unix системах).
На Windows подход чуть иной, и для получения кода завершения лучше использовать CreateProcess() и WaitForSingleObject(), а также GetExitCodeProcess().
ya
^-^
Сообщения: 2675
Зарегистрирован: 16 дек 2021, 19:56

Re: выполнить командную строку из программы

Сообщение ya »

Код: Выделить всё

#include <iostream>
#include <cstdio>
#include <memory>
#include <stdexcept>
#include <string>
#include <array>
#include <sys/wait.h> // Для WIFEXITED, WEXITSTATUS, WIFSIGNALED, WTERMSIG

int exec_and_check(const char* cmd, std::string& output) {
    std::array<char, 128> buffer;
    output.clear();

    // Открываем поток
    std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
    if (!pipe) {
        throw std::runtime_error("popen() не удалось открыть поток");
    }

    // Читаем вывод
    while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
        output += buffer.data();
    }

    // Получаем код завершения процесса
    int status = pclose(pipe.get());
    if (status == -1) {
        // Ошибка вызова pclose()
        throw std::runtime_error("Ошибка при вызове pclose()");
    }

    // Анализируем статус завершения
    if (WIFSIGNALED(status)) {
        int sig = WTERMSIG(status);
        std::cerr << "Программа завершилась аварийно из-за сигнала: " << sig << std::endl;
        return -sig; // возвращаем отрицательное число сигнала
    } else if (WIFEXITED(status)) {
        int exit_status = WEXITSTATUS(status);
        if (exit_status != 0) {
            std::cerr << "Программа завершилась с ошибкой, код: " << exit_status << std::endl;
        }
        return exit_status; // 0 — успешно
    } else {
        // Другие случаи
        return -1;
    }
}

int main() {
    std::string output;
    int result = exec_and_check("./myapp", output);
    std::cout << "Вывод:\n" << output << std::endl;
    if (result == 0) {
        std::cout << "Программа завершилась успешно.\n";
    } else if (result > 0) {
        std::cout << "Программа завершилась с ошибкой, код: " << result << std::endl;
    } else {
        std::cout << "Программа завершилась аварийно, сигнал: " << -result << std::endl;
    }
    return 0;
}
ya
^-^
Сообщения: 2675
Зарегистрирован: 16 дек 2021, 19:56

Re: выполнить командную строку из программы

Сообщение ya »

Код: Выделить всё

#include <iostream>
#include <cstdio>
#include <memory>
#include <stdexcept>
#include <string>
#include <array>
#include <sys/wait.h>

int exec_and_check(const char* cmd, std::string& output) {
    std::array<char, 128> buffer;
    output.clear();

    // Открываем поток
    std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
    if (!pipe) {
        throw std::runtime_error("popen() не удалось открыть поток");
    }

    // Читаем вывод
    while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
        output += buffer.data();
    }

    // Не вызываем pclose() вручную!
    // int status = pclose(pipe.get()); // Удалите эту строку

    // Получаем статус завершения процесса
    int status = -1; // Инициализация
    // Чтобы получить статус, нужно вызвать pclose() один раз
    // Но его вызов уже происходит при уничтожении unique_ptr
    // Поэтому нужно его сохранить
    // Для этого можно использовать explicitly pclose() без unique_ptr или
    // сохранить статус через другую переменную
    
    // Вариант 1: использовать raw pointer и вызов pclose() вручную:
    // Но это не рекомендуется, поскольку управление памятью усложняется

    // Вариант 2: использовать popen() и pclose() без unique_ptr
    // или использовать system() для более простого решения

    // Лучше всего — вызвать pclose() один раз, и получить статус через него
    // Для этого можно сделать так:
    // Не использовать уникальный умный указатель, а хранить FILE* и вызвать pclose() вручную

    // Ниже — исправленная версия, вызывающая pclose() один раз:
    return 0; // placeholder
}

int main() {
    std::string output;
    int result = exec_and_check("./myapp", output);
    std::cout << "Вывод:\n" << output << std::endl;
    if (result == 0) {
        std::cout << "Программа завершилась успешно.\n";
    } else if (result > 0) {
        std::cout << "Программа завершилась с ошибкой, код: " << result << std::endl;
    } else {
        std::cout << "Программа завершилась аварийно, сигнал: " << -result << std::endl;
    }
    return 0;
}
Ответить