Как вызвать команду сортировки UNIX для данных в канале

Я создаю программу на C и с ее помощью настраиваю канал между отдельно разветвленными процессами для межпроцессного взаимодействия.

Первый процесс записал нужные мне данные в канал. Однако, когда второй процесс читает из канала, я пытаюсь выполнить процесс, чтобы он стал командой сортировки UNIX. Я хочу как-то вызвать сортировку данных в канале.

Как я могу вызвать сортировку на канале? В командной строке я могу сортировать, указав имя файла для сортировки в качестве аргумента командной строки, например. "сортировать -r MyFileToSort". Я знаю, что каналы по сути считаются файлами, но они описываются только своим файловым дескриптором, и, насколько мне известно, sort не знает, что делать с fd.

Спасибо за любую помощь/отзыв


person pleaver    schedule 07.01.2015    source источник
comment
Что вы уже пробовали?   -  person Iharob Al Asimi    schedule 07.01.2015
comment
Это может помочь: en.wikipedia.org/wiki/Named_pipe   -  person martin clayton    schedule 07.01.2015
comment
@martinclayton, зачем здесь нужен именованный канал? При необходимости можно просто присоединить FD, возвращаемые mkpipe(), к стандартному вводу или стандартному выводу.   -  person Charles Duffy    schedule 07.01.2015
comment
@Charles помогите != необходимо.   -  person martin clayton    schedule 07.01.2015
comment
@iharob Я пытался создать файл и записать данные из канала в этот файл. Затем я вызываю сортировку этого файла через вызов exec из дочернего процесса и вывод в файл. Это работает, но не совсем то, что я хочу. Вновь созданный файл сначала отображает все содержимое канала, а затем отображает дубликат содержимого канала, за исключением того, что на этот раз отсортировано. По сути, я хочу, чтобы последний выходной файл имел только отсортированное содержимое канала. В командной строке это будет выглядеть так: data | sort › MyOutputFile Я хочу, чтобы данные передавались в команду sort, а затем перенаправлялись в MyOutputFile.   -  person pleaver    schedule 07.01.2015
comment
@martinclayton, конечно, но я не уверен, как это поможет - если только вы не хотите увеличить отток файловой системы (создание и, предположительно, позднее удаление записей каталога) ради самого себя. Если есть какой-то способ, которым использование именованных каналов обеспечивает материальное преимущество по сравнению с безымянными для этого варианта использования, я не могу об этом думать.   -  person Charles Duffy    schedule 07.01.2015
comment
@Chales - я предлагал это только как материал для чтения.   -  person martin clayton    schedule 07.01.2015
comment
@pleaver, ... кстати - если вам не ясно, как это настроить, вы можете прочитать вывод strace bash -c 'generate_data | sort >MyOutputFile', чтобы увидеть, как оболочка выполняет задачу.   -  person Charles Duffy    schedule 07.01.2015
comment
@JonathanLeffler Извините, я новый пользователь. Я очень ценю оказанную быструю помощь. Я хотел бы проголосовать, но у меня еще нет представителя. Я выберу ваш ответ из-за примера кода, но я также ценю ответ Чарльза Даффи.   -  person pleaver    schedule 07.01.2015


Ответы (2)


int p[2];
if (pipe(p) != 0) ...report error and do not continue...
pid_t pid = fork();
if (pid < 0) ...report error, close pipe descriptors, and do not continue...
if (pid == 0)
{
    /* Child - becomes sort */
    dup2(p[0], 0);
    close(p[0]);
    close(p[1]);
    int fd = open("output-file", O_CREAT | O_EXCL | O_WRONLY, 0644);
    if (fd < 0) ...report error and exit...
    dup2(fd, 1);
    close(fd);
    execlp("sort", "sort", (char *)0);
    ...report error and exit...
}
else
{
    /* Parent - writes data to sort */
    close(fd[0]);
    ...write data to fd[1]...
    close(fd[1]);
    int status;
    int corpse;
    while ((corpse = wait(&status)) > 0 && corpse != pid)
        ...consider reporting which child died...
    ...consider reporting sort status...
    ...continue with the rest of the program...
}

Вы можете решить, следует ли сообщать об ошибках, связанных с ошибкой dup2() или ошибкой close(). В любом случае вы ничего не можете сделать, кроме как сообщить о проблеме и выйти. Если кто-то не подверг вашу программу жестокому и необычному наказанию, не предоставив ей стандартный ввод, стандартный вывод и стандартную ошибку (или что-то в другом месте программы закрыло любой из стандартных каналов), то дескрипторы каналов и файлов не могут быть стандартные дескрипторы ввода-вывода, поэтому закрытия безопасны. Если вы не уверены, насколько больны ваши пользователи, вы можете защитить закрытия:

if (p[0] > FILENO_STDERR)
    close(p[0]);

Обычно это излишне параноидально (но может быть интересно попробовать программы с отсутствующим стандартным вводом-выводом).

person Jonathan Leffler    schedule 07.01.2015
comment
Спасибо за пример кода. Я думал, что эта сортировка должна быть явно передана по имени файла, но я вижу, что перенаправление стандартного ввода в канал и просто вызов сортировки работает - person pleaver; 07.01.2015
comment
Многие, но далеко не все программы Unix работают как «фильтры» и читают стандартный ввод, если им не заданы имена файлов, и записывают в стандартный вывод. Вы можете использовать: execlp("sort", "sort", "-o", "output-file", (char *)0); вместо перенаправления ввода-вывода в своем коде. Это заставляет sort писать в output-file. В общем, он позволяет sort записывать в один из своих входных файлов; вы просто используете опцию как побочный эффект, если примете изменение. Если у вас могут быть параметры сортировки, вам следует рассмотреть execvp() или execv() вместо execlp(); таким образом вы можете иметь списки аргументов переменной длины. - person Jonathan Leffler; 07.01.2015

В этом случае вам вообще не нужно передавать sort какие-либо аргументы для указания источника ввода или приемника вывода. Вместо этого перед execing вы должны прикрепить файловые дескрипторы вашего конвейера к его стандартному вводу (FD 0, если данные из канала) или stdout (FD 1, если данные записываются в канал), в зависимости от ситуации.

Для этой цели см. вызов dup2(), который позволяет вам установить место назначения, в которое вы копируете FD. Как указывает @JonathanLeffler, вы должны обязательно закрыть исходные FD (после дублирования их на нужные вам номера) перед вызовом exec.


Поскольку в комментариях вы пояснили, что ваша цель — запись в файл, вы должны присоединить FD 1 к этому целевому файлу перед вызовом exec, а FD 0 прикрепить к выходной стороне конвейера, содержащего ввод.

person Charles Duffy    schedule 07.01.2015
comment
И закройте неиспользуемые файловые дескрипторы перед вызовом exec*() — в данном случае оба файловых дескриптора из pipe(). - person Jonathan Leffler; 07.01.2015
comment
Спасибо, я перенаправил стандартный ввод на считыватель канала, и теперь вызов сортировки без указания файла работает нормально. У меня все еще есть небольшая проблема, но контекст этого вопроса был дан ответ - person pleaver; 07.01.2015