Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

*Almost* Perfect C Shell Piping

Tags:

c

linux

shell

pipe

I am writing a small linux shell in C and am so very close to being done. I take in a command from the user and store it in args, delimited by spaces. In the following example, let's say that args contains the following:

args[] = {"ls", "-l", "|", "wc"};

My function takes in args and also takes in how many pipes there are. I've commented my code as much as possible. Here it is:

int do_command(char **args, int pipes) {
    // The number of commands to run
    const int commands = pipes + 1;
    int i = 0;

    int pipefds[2*pipes];

    for(i = 0; i < pipes; i++){
        if(pipe(pipefds + i*2) < 0) {
            perror("Couldn't Pipe");
            exit(EXIT_FAILURE);
        }
    }

    int pid;
    int status;

    int j = 0;
    int k = 0;
    int s = 1;
    int place;
    int commandStarts[10];
    commandStarts[0] = 0;

    // This loop sets all of the pipes to NULL
    // And creates an array of where the next
    // Command starts

    while (args[k] != NULL){
        if(!strcmp(args[k], "|")){
            args[k] = NULL;
            // printf("args[%d] is now NULL", k);
            commandStarts[s] = k+1;
            s++;
        }
        k++;
    }



    for (i = 0; i < commands; ++i) {
        // place is where in args the program should
        // start running when it gets to the execution
        // command
        place = commandStarts[i];

        pid = fork();
        if(pid == 0) {
            //if not last command
            if(i < pipes){
                if(dup2(pipefds[j + 1], 1) < 0){
                    perror("dup2");
                    exit(EXIT_FAILURE);
                }
            }

            //if not first command&& j!= 2*pipes
            if(j != 0 ){
                if(dup2(pipefds[j-2], 0) < 0){
                    perror("dup2");
                    exit(EXIT_FAILURE);
                }
            }

            int q;
            for(q = 0; q < 2*pipes; q++){
                    close(pipefds[q]);
            }

            // The commands are executed here, 
            // but it must be doing it a bit wrong          
            if( execvp(args[place], args) < 0 ){
                    perror(*args);
                    exit(EXIT_FAILURE);
            }
        }
        else if(pid < 0){
            perror("error");
            exit(EXIT_FAILURE);
        }

        j+=2;
    }

    for(i = 0; i < 2 * pipes; i++){
        close(pipefds[i]);
    }

    for(i = 0; i < pipes + 1; i++){
        wait(&status);
    }
}

My problem is that while the program is kind of executing correctly, it is behaving strangely and I was hoping you could help me out.

For example hen I run ls | wc, the output is that of ls | wc, but then it also prints the output of a simple ls right below it, even though it should just be the wc of the output.

As another example, when I try ls -l | wc, the first number of wc shows up, but then the output of ls -l shows up below it, even though it should just be the wc of the output.

Thanks in advance! :)

like image 593
Rick_Sch Avatar asked Oct 01 '12 18:10

Rick_Sch


People also ask

What is a shell in C?

The C shell is an interactive command interpreter and a command programming language. It uses syntax that is similar to the C programming language. The csh command starts the C shell.

What is piping in Shell?

A pipe is a form of redirection (transfer of standard output to some other destination) that is used in Linux and other Unix-like operating systems to send the output of one command/program/process to another command/program/process for further processing.


1 Answers

Okay, I have found one little error. This

       if( execvp(args[place], args) < 0 ){

should be

       if( execvp(args[place], args+place) < 0 ){

Your version used the args for the first command for all the others. Other than that, it works for me.

like image 154
jpalecek Avatar answered Sep 27 '22 15:09

jpalecek