Чтобы соединить несколько команд вместе, вам нужно, чтобы родительский узел работал для каждой команды fork()
.
Используя for
l oop, вам необходимо сделать это для первых n - 1
команд (последняя будет выполнена в основной программе):
- Создать канал.
- Выполнить
fork()
. - В дочернем: перезаписать стандартный ввод с конца чтения предыдущего канала и стандартный вывод с записью конца текущего канала.
- В дочернем: execute
execve()
. - В родительском объекте: закрыть ненужные каналы и сохранить конец чтения текущей трубы, который будет использоваться в следующем цикле.
Затем, после окончания l oop, перезаписать стандартный ввод с конца чтения последнего канала и выполнения execve()
последней команды.
Ниже я написал простой рабочий пример, который выполняет:
ls | wc -l | xargs printf "0x%x\n" | cowsay
Это должно работать для любого количества команд (включая только одну отдельную команду).
ПРИМЕЧАНИЕ : I не добавлял проверки ошибок в этом коде отдельно для execvp()
только для того, чтобы сделать его коротким, но вы обязательно должны проверять ошибки после каждого вызова pipe()
, dup2()
, fork()
и любой другой функции.
Код:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define MAX_ARGC 3
int main(void) {
char *commands[][MAX_ARGC + 1] = {
{"ls", NULL},
{"wc", "-l", NULL},
{"xargs", "printf", "0x%x\n", NULL},
{"cowsay", NULL}
};
size_t i, n;
int prev_pipe, pfds[2];
n = sizeof(commands) / sizeof(*commands);
prev_pipe = STDIN_FILENO;
for (i = 0; i < n - 1; i++) {
pipe(pfds);
if (fork() == 0) {
// Redirect previous pipe to stdin
if (prev_pipe != STDIN_FILENO) {
dup2(prev_pipe, STDIN_FILENO);
close(prev_pipe);
}
// Redirect stdout to current pipe
dup2(pfds[1], STDOUT_FILENO);
close(pfds[1]);
// Start command
execvp(commands[i][0], commands[i]);
perror("execvp failed");
exit(1);
}
// Close read end of previous pipe (not needed in the parent)
close(prev_pipe);
// Close write end of current pipe (not needed in the parent)
close(pfds[1]);
// Save read end of current pipe to use in next cycle
prev_pipe = pfds[0];
}
// Get stdin from last pipe
if (prev_pipe != STDIN_FILENO) {
dup2(prev_pipe, STDIN_FILENO);
close(prev_pipe);
}
// Start last command
execvp(commands[i][0], commands[i]);
perror("execvp failed");
exit(1);
}
Вывод на мою машину (поскольку ls
вернул 41 == 0x29 строк):
______
< 0x29 >
------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||