Оптимизация захвата stdout из команды system()

Я пытаюсь запустить внешнее приложение через system() - например, system("ls"). Я хотел бы записать его вывод, поскольку это происходит, поэтому я могу отправить его на другую функцию для дальнейшей обработки. Какой лучший способ сделать это в C/С++?

Ответ 1

В раскрывающемся руководстве:

#include <stdio.h>

FILE *popen(const char *command, const char *type);

int pclose(FILE *stream);

Ответ 2

Попробуйте функцию popen(). Он выполняет команду, например system(), но направляет вывод в новый файл. Возвращается указатель на поток.

  FILE *lsofFile_p = popen("lsof", "r");

  if (!lsofFile_p)
  {
    return -1;
  }

  char buffer[1024];
  char *line_p = fgets(buffer, sizeof(buffer), lsofFile_p);
  pclose(lsofFile_p);

Ответ 3

РЕДАКТИРОВАТЬ: неверный вопрос, поскольку вы хотите передать вывод в другую программу, а не другую функцию. popen() почти наверняка вы хотите.

Система предоставляет полный доступ к оболочке. Если вы хотите продолжить его использование, вы можете  перенаправить его на временный файл, по системе ( "ls > tempfile.txt" ), но выбор защищенного временного файла - это боль. Или вы можете перенаправить его через другую программу: system ( "ls | otherprogram" );

Некоторые могут рекомендовать команду popen(). Это то, что вы хотите, если вы сами можете обработать результат:

FILE *output = popen("ls", "r");

который даст вам указатель FILE, с которым вы можете читать, с выходом команды на нем.

Вы также можете использовать вызов pipe() для создания соединения в сочетании с fork() для создания новых процессов dup2() для изменения стандартного ввода и вывода из них, exec() для запуска новых программ и wait() в основной программе, чтобы дождаться их. Это просто настройка трубопровода так же, как и оболочка. См. Справочную страницу pipe() для получения более подробной информации и примера.

Ответ 4

Функции popen() и такие не перенаправляют stderr и такие; Я написал popen3() для этой цели.

Ответ 5

Наиболее эффективным способом является использование дескриптора файла stdout напрямую, минуя FILE поток:

pid_t popen2(const char *command, int * infp, int * outfp)
{
    int p_stdin[2], p_stdout[2];
    pid_t pid;

    if (pipe(p_stdin) == -1)
        return -1;

    if (pipe(p_stdout) == -1) {
        close(p_stdin[0]);
        close(p_stdin[1]);
        return -1;
    }

    pid = fork();

    if (pid < 0) {
        close(p_stdin[0]);
        close(p_stdin[1]);
        close(p_stdout[0]);
        close(p_stdout[1]);
        return pid;
    } else if (pid == 0) {
        close(p_stdin[1]);
        dup2(p_stdin[0], 0);
        close(p_stdout[0]);
        dup2(p_stdout[1], 1);
        dup2(::open("/dev/null", O_WRONLY), 2);

        /// Close all other descriptors for the safety sake.
        for (int i = 3; i < 4096; ++i) {
            ::close(i);
        }

        setsid();
        execl("/bin/sh", "sh", "-c", command, NULL);
        _exit(1);
    }

    close(p_stdin[0]);
    close(p_stdout[1]);

    if (infp == NULL) {
        close(p_stdin[1]);
    } else {
        *infp = p_stdin[1];
    }

    if (outfp == NULL) {
        close(p_stdout[0]);
    } else {
        *outfp = p_stdout[0];
    }

    return pid;
}

Чтобы прочитать вывод из использования детьми popen2() следующим образом:

int child_stdout = -1;
pid_t child_pid = popen2("ls", 0, &child_stdout);

if (!child_pid) {
    handle_error();
}

char buff[128];
ssize_t bytes_read = read(child_stdout, buff, sizeof(buff));

Как для записи, так и для чтения:

int child_stdin = -1;
int child_stdout = -1;
pid_t child_pid = popen2("grep 123", &child_stdin, &child_stdout);

if (!child_pid) {
    handle_error();
}

const char text = "1\n2\n123\n3";
ssize_t bytes_written = write(child_stdin, text, sizeof(text) - 1);

char buff[128];
ssize_t bytes_read = read(child_stdout, buff, sizeof(buff));

Ответ 6

В Windows вместо использования system() используйте CreateProcess, перенаправьте вывод на канал и подключитесь к нему.

Я предполагаю, что это возможно и в некоторых вариантах POSIX?

Ответ 7

Функции popen() и pclose() могут быть тем, что вы ищете.

Посмотрите на руководство glibc для примера.

Ответ 8

Собственно, я только что проверил и:

  • popen проблематичен, потому что процесс разветвляется. Поэтому, если вам нужно дождаться выполнения команды оболочки, тогда вам будет не хватать ее. В моем случае моя программа была закрыта еще до того, как труба приступила к работе.

  • В итоге я использовал системный вызов с командой tar в linux. Возвращаемое значение из системы было результатом tar.

Итак: , если вам нужно вернуть значение, тогда не только нет необходимости использовать popen, он, вероятно, не будет делать то, что вы хотите.

Ответ 9

На этой странице: capture_the_output_of_a_child_process_in_c описывает ограничения использования popen vs. using fork/exec/dup2/STDOUT_FILENO.

У меня возникают проблемы вывод tshark с помощью popen.

И я предполагаю, что это ограничение может быть моей проблемой:

Он возвращает поток stdio, а не файловый дескриптор, который не подходит для обработки вывода асинхронно.

Я вернусь к этому ответу, если у меня есть решение с другим подходом.

Ответ 10

Я не совсем уверен, что это возможно в стандартном C, поскольку два разных процесса обычно не разделяют пространство памяти. Самый простой способ, которым я могу это сделать, это заставить вторую программу перенаправить свой вывод в текстовый файл (programname > textfile.txt), а затем прочитать этот текстовый файл для обработки. Однако это может быть не лучшим образом.