5
\$\begingroup\$

I am writing a simpel program that can basicly do the same as piping two linux commands can do. For example, ls -l | grep vars.sh, this can be done on the Linux console. I need to write a programm in C that can do the same. I think I got it working but I want to know if I am doing it rite and if there are some modifications I can do on my code.

Here is my code:

#include <stdio.h> // Voor verschillende I/O's, macro's, declaraties.
#include <stdlib.h> // Om de system call te activeren.
#include <unistd.h> // Laden van constanten en types.

int main()
{
    pid_t pid;
    int pipefd[2];
    pipe(pipefd);
    pid = fork();

    if(pid==-1){
        perror("pipe");
        exit(EXIT_FAILURE);
    }

    if(pid>0){
        close(pipefd[0]);
        dup2(pipefd[1],1);
        execlp("ls","ls","-l",NULL);
    }
    else{
        close(pipefd[1]);
        dup2(pipefd[0],0);
        execlp("grep","grep",".bashrc",NULL);
        exit(EXIT_FAILURE);
    }
}

Any feedback,rewrites or modifications are welcome.

\$\endgroup\$

migrated from stackoverflow.com Jul 22 '11 at 16:24

This question came from our site for professional and enthusiast programmers.

  • \$\begingroup\$ Homework problem? \$\endgroup\$ – Carl Norum Jun 16 '11 at 20:48
  • \$\begingroup\$ Code looks fine, not entirely clear what your question is. \$\endgroup\$ – chrisdowney Jun 16 '11 at 20:49
  • \$\begingroup\$ Looks like you are doing it rite :-) \$\endgroup\$ – Vlad Jun 16 '11 at 20:50
  • 1
    \$\begingroup\$ Yea a homework problem :) I just want to be able to do command | command that works on Linux in a C program. The code works, but is there another way to do it by modifying the code? As in can this be done in a other way with C? \$\endgroup\$ – bryan Jun 16 '11 at 20:55
  • 2
    \$\begingroup\$ You don't do nearly enough plumbing; you need to close a lot more pipe file descriptors. If you dup2() a descriptor, you normally want to close the starting descriptor afterwards. \$\endgroup\$ – Jonathan Leffler Jun 16 '11 at 22:24
1
\$\begingroup\$

The only other way that I could see this can be done in POSIX would be to use popen() rather than using raw pipes. It also returns a stream pointer that can be used with the C standard I/O functions like fprintf(), fgets(), etc. to read and/or write to the process on the other-side of the pipe created by popen(). According to the POSIX specification, with popen(),

The environment of the executed command shall be as if a child process were created within the popen() call using the fork() function, and the child invoked the sh utility using the call:

execl(shell path, "sh", "-c", command, (char *)0);

where shell path is an unspecified pathname for the sh utility.

So it's basically an easier, more straight-forward way to work with pipes rather than having to fork child processes yourself and close the un-used portions of the pipe so that you don't end up with accidental blocking reads or lost writes because there are still active file-descriptors on each end of the pipe that you've accidentally forgotten to close in either the parent or child process.

With your example, using popen() would look like the following:

#include <stdio.h>
#define BUFFERSIZE 128

FILE *ls_proc, *grep_proc;
ls_proc = popen("ls -l", "r");
grep_proc = popen("grep .bashrc", "w");

char buffer[BUFFERSIZE];

//read from one child process and write to the other
while (fgets(buffer, BUFFERSIZE, ls_proc) != NULL)
    fprintf(grep_proc, "%s", buffer);

pclose(ls_proc);
pclose(grep_proc);    
\$\endgroup\$
  • \$\begingroup\$ Thanks for the answer, though if I copy paste this in a main () function I get all sorts of errors when compiling. the FILE* gives an error and the fprintf statement gives a error \$\endgroup\$ – bryan Jun 17 '11 at 5:09
  • \$\begingroup\$ Sorry, a small typo in the declaration of grep_proc where I forgot its * symbol because it's a pointer to a stream. You can copy and paste this code now and it should work. \$\endgroup\$ – Jason Jun 17 '11 at 12:55
  • \$\begingroup\$ This will not work with binary data that is passed through the pipe.fgets and fprintf only work on null terminal strings. Also, this requires the parent process to do the actual transfer of data in the while loop while the original example does not need a while loop to transfer data from one process to another (the system does the work for you). \$\endgroup\$ – Mark Lakata Jun 28 at 21:16
1
\$\begingroup\$

Your code looks fine to me. Since you're asking, I'd suggest one tiny thing; that you check the calls return values. pipe() and dup2() can fail too, you want to know when that happens.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy