Возврат вывода из bash script к вызову функции С++

Я пишу детскую программу для практики. То, что я пытаюсь выполнить, - это простой небольшой графический интерфейс, который отображает службы (для Linux); с кнопками для запуска, остановки, включения и отключения служб (так же, как вкладка msconfig "Службы" в Windows). Я использую С++ с Qt Creator на Fedora 21.

Я хочу создать графический интерфейс с С++ и заполнить графический интерфейс списком сервисов, вызвав скрипты bash и вызывая скрипты bash при нажатии кнопки, чтобы выполнить соответствующие действия (включить, отключить и т.д.).

Но когда С++ GUI вызывает bash script (используя system("path/to/script.sh")), возвращаемое значение будет только для успеха выхода. Как получить вывод самого script, чтобы я мог, в свою очередь, использовать его для отображения в графическом интерфейсе?

Для концептуального примера: если я пытаюсь отобразить вывод (systemctl --type service | cut -d " " -f 1) в графическом интерфейсе, который я создал на С++, как бы я это сделал? Это даже правильный способ сделать то, что я пытаюсь выполнить? Если нет,

  • Каков правильный путь? и
  • Есть ли способ сделать это, используя мой текущий метод?

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

Ответ 1

Мы будем использовать функцию popen здесь.

std::string exec(char* cmd) {
    FILE* pipe = popen(cmd, "r");
    if (!pipe) return "ERROR";
    char buffer[128];
    std::string result = "";
    while(!feof(pipe)) {
        if(fgets(buffer, 128, pipe) != NULL)
            result += buffer;
    }
    pclose(pipe);
    return result;
}

Эта функция принимает команду в качестве аргумента и возвращает результат как string.

ПРИМЕЧАНИЕ: это не будет захватывать stderr! Быстрое и легкое обходное решение - перенаправить stderr на stdout, с 2>&1 в конце вашей команды.

Здесь есть документация по popen. Счастливое кодирование:)

Ответ 2

Вам нужно запустить команды с помощью popen вместо system, а затем прокрутить возвращаемый указатель файла.

Вот простой пример для команды ls -l

#include <stdio.h>
#include <stdlib.h>

int main() {
    FILE *process;
    char buff[1024];

    process = popen("ls -l", "r");

    if (process != NULL) {
        while (!feof(process)) {
            fgets(buff, sizeof(buff), process);
            printf("%s", buff);
        }

        pclose(process);
    }

    return 0;
}

Ответ 3

Длительный подход - который дает вам полный контроль над stdin, stdout и stderr дочернего процесса за счет довольно значительной сложности - предполагает использование fork и execve напрямую.

  • До fork ing, настройте свои конечные точки для связи - pipe работает хорошо или socketpair. Предполагаю, что вы выписали что-то вроде:

    int childStdin[2], childStdout[2], childStderr[2];
    pipe(childStdin);
    pipe(childStdout);
    pipe(childStderr);
    
  • После fork в дочернем процессе до execve:

    dup2(childStdin[0], 0);  // childStdin read end to fd 0 (stdin)
    dup2(childStdout[1], 1); // childStdout write end to fd 1 (stdout)
    dup2(childStderr[1], 2); // childStderr write end to fd 2 (stderr)
    

    .. затем закройте все childStdin, childStdout и childStderr.

  • После fork в родительском процессе:

     close(childStdin[0]);  // parent cannot read from stdin
     close(childStdout[1]); // parent cannot write to stdout/stderr
     close(childStderr[1]);
    

Теперь ваш родительский процесс имеет полный контроль над std i/o дочернего процесса - и должен безопасно мультиплексировать childStdin[1], childStdout[0] и childStderr[0], а также отслеживать для SIGCLD и в конечном итоге использовать wait -series вызывает проверку кода завершения процесса. pselect особенно хорош для работы с SIGCLD при асинхронной работе с std i/o. См. Также select или poll, конечно.

Если вы хотите объединить дочерний элемент stdout и stderr, просто dup2(childStdout[1], 2) и полностью избавиться от childStderr.

Человеческие страницы должны заполнить пробелы отсюда. Так что это трудно, если вам это нужно.