2

I'm trying to make a program that executes the following commands connecting the output of one to the input of the next using pipes and taking two arguments DIR (directory) and ARG (filetype, example: jpg).

ls DIR -laR | grep ARG | sort

Here's the code:

int main(int argc, char *argv[]) {

    if (argc != 3) {
        printf("Invalid arguments. <dir> <arg>\n");
        exit(1);
    }

    int pipe_fd1[2];
    int pipe_fd2[2];
    pid_t ls_pid, grep_pid;
    int status;

    pipe(pipe_fd1);
    pipe(pipe_fd2);

    ls_pid = fork();
    if (ls_pid == 0) { //first child ls DIR -laR
        dup2(pipe_fd1[1], STDOUT_FILENO);
        close(pipe_fd1[0]);

        execlp("ls", "ls", argv[1], "-laR", NULL);

    } else if (ls_pid > 0) {
        grep_pid = fork();
        if (grep_pid == 0) { //second child grep ARG
            dup2(pipe_fd1[0], STDIN_FILENO);
            dup2(pipe_fd2[1], STDOUT_FILENO);       
            close(pipe_fd1[1]);
            close(pipe_fd2[0]);

            waitpid(ls_pid, &status, 0);    
            execlp("grep", "grep", argv[2], NULL);

        } else if (grep_pid > 0) { //parent sort
            dup2(pipe_fd2[0], STDIN_FILENO);
            close(pipe_fd2[1]);

            waitpid(grep_pid, &status, 0);
            execlp("sort", "sort", NULL);
        }

    }

    return 0;
}

It seems to be stuck? Not sure why?

0kazaki
  • 23
  • 1
  • 7
  • Why do you wait for `grep` to terminate before you invoke `sort`? – fuz Jun 13 '15 at 20:23
  • On my system, the program is stuck on the line `waitpid(grep_pid, &status, 0);` as `grep` never terminates. Why don't you close the file descriptors associated with the first pipe in the parent process? – fuz Jun 13 '15 at 20:24

1 Answers1

3

You never close pipe_fd1 on the parent, so grep and sort doen't know when to stop reading input: because the pipe read and write ends are never closed on the parent, the reader blocks waiting for more input that will never arrive. You need to close it.

Also, you don't need waitpid(): the way pipes work ensures that input flows linearly and in order throughout the pipe.

Here's the working version with these issues addressed:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char *argv[]) {

    if (argc != 3) {
        printf("Invalid arguments. <dir> <arg>\n");
        exit(1);
    }

    int pipe_fd1[2];
    int pipe_fd2[2];
    pid_t ls_pid, grep_pid;

    pipe(pipe_fd1);

    ls_pid = fork();
    if (ls_pid == 0) { //first child ls DIR -laR
        dup2(pipe_fd1[1], STDOUT_FILENO);
        close(pipe_fd1[0]);
        execlp("ls", "ls", argv[1], "-laR", NULL);

    } else if (ls_pid > 0) {
        dup2(pipe_fd1[0], STDIN_FILENO);
        close(pipe_fd1[1]);

        pipe(pipe_fd2);
        grep_pid = fork();

        if (grep_pid == 0) { //second child grep ARG
            dup2(pipe_fd2[1], STDOUT_FILENO);
            close(pipe_fd2[0]);  
            execlp("grep", "grep", argv[2], NULL);

        } else if (grep_pid > 0) { //parent sort
            dup2(pipe_fd2[0], STDIN_FILENO);
            close(pipe_fd2[1]);
            execlp("sort", "sort", NULL);
        }

    }

    return 0;
}
Filipe Gonçalves
  • 20,783
  • 6
  • 53
  • 70
  • Thank you for the help, but I don't understand why the parent needs to close `pipe_fd1` when I never do any redirections using `pipe_fd1` on the parent. – 0kazaki Jun 13 '15 at 20:43
  • @0kazaki Because as long as there is at least one one writer in the pipe, the reader process(es) will never see end of input, even if that writer (the parent) doesn't actually write. When the last writer closes the pipe, readers will see that the input is over and proceed (`sort` needs to consume all the input before showing the results). – Filipe Gonçalves Jun 13 '15 at 20:51