Here is an example of the code that executes a pipe ls /etc/ | awk '{print $9, $5}' | grep '^profile'
.
The output of each process is used for the input in the next process. The first process of the pipe has no entry (but could if necessary), and the output of the last process is read by the main process (your shell):
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define MAX_ARGS 10 // FIXME debe ser dinámico
char* const cmds[][MAX_ARGS] = {
{ "ls", "-l", "/etc", NULL },
{ "awk", "{print $9, $5;}", NULL }, // columna $9 en ls salida es el nombre de fichero, $5 - su tamaño
{ "grep", "^profile", NULL },
};
// cerrar todos los descriptores abiertos desde 'start'
void cerrar_fds(int start) {
// Hay métodos mejores para identificar cuales ficheros son abiertos,
// mira en http://stackoverflow.com/questions/6583158/finding-open-file-descriptors-for-a-process-linux-c-code
// pero es bastante para nuestros deberes
int i;
for(i = start + 1; i < 256; i++) {
(void)close(i); // el éxito no importa
}
}
pid_t ejec_part(char* const cmd[], int entrada, int* salida) {
pid_t pid;
int salidaPipe[2];
if (pipe(salidaPipe) == -1) {
perror("pipe");
exit(98);
}
// ahora tenemos un pipe con dos lados, salidaPipe[0] es para leer
// y salidaPipe[1] es para escribir
pid = fork();
if (pid == 0) { // proceso hijo
if (entrada != -1) {
// dup2 es mejor que dup porque permite especificar
// a cual descriptor copiar el original
if (dup2(entrada, 0) == -1) {
perror("dup2");
exit(100);
}
} else {
close(0);
}
// aquí salidaPipe[0] no se necesita
close(salidaPipe[0]);
// creamos una copia de salidaPipe[1] en FD == 1 (stdout)
if (dup2(salidaPipe[1], 1) == -1) {
perror("dup2");
exit(100);
}
cerrar_fds(1);
execvp(cmd[0], cmd);
// normalmente no vamos hasta aquí
perror("execvp");
exit(101);
} else if (pid < 0) { // error
perror("fork");
exit(99);
} else {
// en proceso padre salidaPipe[1] no se necesita
close(salidaPipe[1]);
*salida = salidaPipe[0];
return pid;
}
}
int main(void) {
// ls no necesita la entrada, por eso usamos -1 para la entrada
int entradaFd = -1;
int salidaFd = -1;
int numCmds = sizeof(cmds) / sizeof(cmds[0]);
int i;
for(i = 0; i < numCmds; i++) {
ejec_part(cmds[i], entradaFd, &salidaFd);
// un próximo proceso usa salidaFd del proceso anterior (el lado para leer)
// como su entrada
entradaFd = salidaFd;
}
{
FILE* res = fdopen(entradaFd, "rb");
int bufTam = 100;
char* buf = malloc(bufTam);
while(-1 != (getline(&buf, &bufTam, res))) {
printf("recibido de pipe: %s", buf);
}
fclose(res);
free(buf);
}
return 0;
}