Перенаправление вывода exec в буфер или файл - PullRequest
38 голосов
/ 09 апреля 2010

Я пишу программу на C, где я fork(), exec() и wait(). Я хотел бы взять вывод программы, которую я выполнил, чтобы записать ее в файл или буфер.

Например, если я исполняю ls, я хочу записать file1 file2 etc в буфер / файл. Я не думаю, что есть способ прочитать стандартный вывод, значит ли это, что я должен использовать канал? Есть ли здесь общая процедура, которую я не смог найти?

Ответы [ 4 ]

80 голосов
/ 09 апреля 2010

Для отправки вывода в другой файл (я опускаю проверку ошибок, чтобы сосредоточиться на важных деталях):

if (fork() == 0)
{
    // child
    int fd = open(file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);

    dup2(fd, 1);   // make stdout go to file
    dup2(fd, 2);   // make stderr go to file - you may choose to not do this
                   // or perhaps send stderr to another file

    close(fd);     // fd no longer needed - the dup'ed handles are sufficient

    exec(...);
}

Для отправки вывода в канал, чтобы вы могли затем прочитать вывод в буфер:

int pipefd[2];
pipe(pipefd);

if (fork() == 0)
{
    close(pipefd[0]);    // close reading end in the child

    dup2(pipefd[1], 1);  // send stdout to the pipe
    dup2(pipefd[1], 2);  // send stderr to the pipe

    close(pipefd[1]);    // this descriptor is no longer needed

    exec(...);
}
else
{
    // parent

    char buffer[1024];

    close(pipefd[1]);  // close the write end of the pipe in the parent

    while (read(pipefd[0], buffer, sizeof(buffer)) != 0)
    {
    }
}
14 голосов
/ 09 апреля 2010

Вы должны точно решить, что вы хотите сделать, и, желательно, объяснить это более четко.

Вариант 1: Файл

Если вы знаете, к какому файлу хотите получить вывод выполненной команды, тогда:

  1. Убедитесь, что родитель и ребенок согласовывают имя (родитель выбирает имя перед разветвлением).
  2. Родительские вилки - у вас есть два процесса.
  3. Дочерний процесс реорганизует вещи так, что дескриптор файла 1 (стандартный вывод) попадает в файл.
  4. Обычно вы можете оставить стандартную ошибку в покое; вы можете перенаправить стандартный ввод из /dev/null.
  5. Потом ребенок выполняет соответствующую команду; указанная команда выполняется, и любой стандартный вывод поступает в файл (это основное перенаправление ввода-вывода оболочки).
  6. Выполненный процесс завершается.
  7. Между тем, родительский процесс может принять одну из двух основных стратегий:
    • Откройте файл для чтения и продолжайте чтение, пока он не достигнет EOF. Затем необходимо дважды проверить, умер ли ребенок (чтобы больше не было данных для чтения), или зависать в ожидании большего ввода от ребенка.
    • Подождите, пока ребенок умрет, а затем откройте файл для чтения.
    • Преимущество первого состоит в том, что родитель может выполнять некоторую часть своей работы, когда ребенок также бежит; Преимущество второго состоит в том, что вам не нужно дурачиться с системой ввода / вывода (многократно читая последние EOF).

Вариант 2: Труба

Если вы хотите, чтобы родитель прочитал выходные данные дочернего элемента, организуйте, чтобы дочерний элемент передавал свои выходные данные родителю.

  1. Используйте popen (), чтобы сделать это простым способом. Он запустит процесс и отправит вывод вашему родительскому процессу. Обратите внимание, что родительский элемент должен быть активным, пока дочерний элемент генерирует выходные данные, поскольку каналы имеют небольшой размер буфера (часто 4-5 КБ), и если дочерний элемент генерирует больше данных, чем тот, в то время как родительский элемент не читает, дочерний элемент будет блокироваться до тех пор, пока родитель читает. Если родитель ожидает смерти ребенка, у вас тупик.
  2. Используйте pipe () и т.д., чтобы сделать это сложным путем. Родитель вызывает pipe (), затем разветвляется. Дочерний объект сортирует трубопровод так, что конец записи канала является его стандартным выводом и обеспечивает закрытие всех других файловых дескрипторов, связанных с каналом. Это вполне может использовать системный вызов dup2 (). Затем он выполняет требуемый процесс, который отправляет свой стандартный вывод по конвейеру.
  3. Тем временем родитель также закрывает нежелательные концы канала и начинает читать. Когда он получает EOF на трубе, он знает, что ребенок закончил и закрыл трубу; он также может закрыть свой конец трубы.
2 голосов
/ 09 апреля 2010

Поскольку вы выглядите так, как будто собираетесь использовать это в среде Linux / Cygwin, вы хотите использовать popen . Это похоже на открытие файла, только вы получите исполняемые программы stdout, поэтому вы можете использовать свои обычные fscanf, fread и т. Д.

1 голос
/ 09 апреля 2010

После разветвления используйте dup2(2) для дублирования FD файла в FD stdout, затем exec.

...