Можно ли дублировать дескриптор файла несколько раз? - PullRequest
1 голос
/ 24 сентября 2019

Я долго искал и не могу найти ответ на свой вопрос.

Я пытаюсь воспроизвести оболочку на C с полным перенаправлением.Чтобы сделать это, я хотел открыть файл перед выполнением моей команды.

Например, в ls > file1 > file2 я использую dup2(file1_fd, 1) и dup2(file2_fd, 1), а затем выполняю ls для заполненияфайлы, но кажется, что стандартный вывод может быть открыт только один раз, поэтому будет заполнен только file2, потому что он был последним, который был продублирован.

Есть ли способ перенаправить стандартный вывод в несколько файлов?

Что-то мне не хватает?Спасибо!

Ответы [ 3 ]

2 голосов
/ 24 сентября 2019

Есть ли способ перенаправить стандартный вывод на несколько файлов?

Многие файловые дескрипторы нельзя сделать одним файловым дескриптором.Вам нужно написать в каждый файловый дескриптор отдельно.Это то, что утилита tee делает для вас.

1 голос
/ 26 сентября 2019

Стандартный вывод не отличается от любого другого открытого файла, единственная особенность - это дескриптор файла 1 (поэтому в вашем процессе может быть только один дескриптор файла с индексом 1). Вы можете dup(2) дескриптор файла1 чтобы получить, скажем, дескриптор файла 6.В этом и заключается задача dup() просто получить другой дескриптор файла (с другим номером), чем тот, который вы используете в качестве источника, , но для того же источника .Опущенные дескрипторы позволяют безразлично использовать любой из опущенных дескрипторов для вывода или изменять открытые флаги, такие как закрывать при exec флаг или неблокировать или append flag (не все являются общими, я не уверен, какие из них можно изменить, не затрагивая другие в дупле).Они совместно используют указатель файла, поэтому каждый write(), который вы пытаетесь использовать для любого из файловых дескрипторов, будет обновляться в других.

Но идея перенаправления не в этом.Соглашение в Unix гласит, что каждая программа получит три дескриптора , уже открытых от своего родительского процесса.Итак, чтобы использовать разветвление, сначала вам нужно подумать о том, как написать нотацию для выражения того, что программа получит (уже открыл) более одного выходного потока (чтобы вы могли перенаправить любой из них правильно, перед вызовом программы ) То же самое относится и к объединению потоков.Здесь проблема более сложная, так как вам нужно выразить, как потоки данных могут быть объединены в один, и это делает проблему объединения зависимой от проблемы.

Файл dup() pingне способ заставить файловый дескриптор записывать в два файла ... но наоборот, это способ сделать два разных файловых дескриптора для ссылки на один и тот же файл .

Единственный способ сделать то, что вы хотите, - это дублировать write(2) вызовов на каждом дескрипторе файла, который вы собираетесь использовать.

Как прокомментировал какой-то ответ, команда tee(1) позволяет вам разветвлять поток данных.в канале, но не с файловыми дескрипторами, tee(1) просто открывает файл и write(2) s вводит весь ввод, в дополнение к записи (2), также выводит его на стандартный вывод.

Там нетпредоставление для разветвления потоков данных в оболочке, так как нет условий для объединения (в частности) потоков данных на входе.Я думаю, что это некоторая заброшенная идея в дизайне оболочки Стива Борна, и вы, вероятно, дойдете до той же точки.

Кстати, просто изучите возможность использования общего оператора dup2(), который является <<em> n >& m >, но, опять же, учтите, что для программы перенаправления 2>&3 2>&4 2>&5 2>&6 означает, что вы предварительно открыли 7 файловых дескрипторов, 0 ...6, в котором stderr является псевдонимом дескрипторов 3 до 6 (поэтому любые данные, записанные в любой из этих дескрипторов, появятся в том, что было stderr), или вы можете использовать 2<file_a 3<file_b 4<file_c, что означает, что ваша программа будетвыполняется с дескриптором файла 2 (stderr), перенаправленным с file_a, а дескрипторы файла 3 и 4 уже открыты из файлов file_b и file_c.Вероятно, некоторые нотации должны быть разработаны (и сейчас мне не легко понять, как их разработать), чтобы обеспечить возможность передачи (с помощью системного вызова pipe(2)) между различными процессами, которые были запущены для выполнения какой-либо задачи,но вам нужно построить общий граф, чтобы учесть общность.

1 голос
/ 24 сентября 2019

Вы запрашиваете точную причину существования команды tee (вы можете посмотреть ее исходный код здесь ).

Вы не можете дублировать дескриптор файла, используя dup2() несколько раз.Как вы уже видели, последний перезаписывает любое предыдущее дублирование.Поэтому вы не можете перенаправить вывод программы в несколько файлов напрямую, используя dup2().

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

Вот очень простой пример того, как вы могли бы это сделать:

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

#define N 4096

int main(int argc, const char *argv[]) {
    FILE *fp1, *fp2, *pipe;

    fp1 = fopen("out1.txt", "w");
    if (fp1 == NULL) {
        perror("fopen out1 failed");
        return 1;
    }

    fp2 = fopen("out2.txt", "w");
    if (fp2 == NULL) {
        perror("fopen out2 failed");
        return 1;
    }

    // Run `ls -l` just as an example.
    pipe = popen("ls -l", "r");

    if (pipe == NULL) {
        perror("popen failed");
        return 1;
    }

    size_t nread, nwrote;
    char buf[N];

    while ((nread = fread(buf, 1, N, pipe))) {
        nwrote = 0;
        while (nwrote < nread)
            nwrote += fwrite(buf + nwrote, 1, nread - nwrote, fp1);

        nwrote = 0;
        while (nwrote < nread)
            nwrote += fwrite(buf + nwrote, 1, nread - nwrote, fp2);
    }

    pclose(pipe);
    fclose(fp2);
    fclose(fp1);

    return 0;
}

Приведенный выше код предназначен только для приблизительной оценки того, как все это работает, он не проверяет некоторые ошибкив fread, fwrite и т. д .: конечно, вы должны проверять наличие ошибок в окончательной программе.

Также легко увидеть, как это можно расширить для поддержки произвольного числа выходных файлов (просто используямассив FILE *).

...