Question

I am so close to figuring out a program I have been writing for linux shell written in C. I have been wanting to get this working for a while now, and I decided to pick it up again and have been tinkering with it for the past few weeks.

For the following code, keep in mind that the array called arrayOfCommands is dynamically filled. My code fills the arrayOfCommands with the current command being run. For the sake of my example, we will be running the command ls -l | wc and arrayOfCommands is filled with the following, depending on which time through the loop it is:

//Pass #1
arrayOfCommands[]= ("ls", "-l", NULL)

//Pass #2
arrayOfCommands[]= ("wc", NULL)

Here is what I have so far:

//PIPING
int do_command(char **args, int pipes) {
    // pipes is the number of pipes in the command
    // (In our example, one)


    // The number of commands is one more than the
    // number of pipes (In our example, two)
    const int commands = pipes + 1;  //Ex: 2
    int i = 0;

    // Set up the pipes
    int pipefds[2*pipes];

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

    // Variables
    int pid;
    int status;
    char *str_ptr;

    int j = 0;
    for (i = 0; i < commands; ++i) {

        // A magic function that updates arrayOfCommands with
        // the current command goes here.  It doesn't make 
        // sense in the context of the code, so just believe me! :)

        // Ex: The contents will be "ls -l" or "wc" depending on 
        // which time through the loop we are

        pid = fork();

        if(pid == 0) {
            //if not last command
            if(i < commands){
                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);
                }
            }

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

            // Should any of the below inputs be *arrayOfCommands or 
            // **arrayOfCommands or &arrayOfCommands?
            // I'm REALLY bad with pointers

            if( execvp(arrayOfCommands, arrayOfCommands) < 0 ){
                    perror(arrayOfCommands);
                    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);
}

When I run this, I get a couple of errors:

  • dup2: Bad file descriptor
  • ls: |: No such file or directory
  • ls: wc: No such file or directory

Could someone help me figure out the following two things:

  1. Why am I getting these errors?
  2. In the execvp function, what kinds if pointers am I looking for? arrayOfCommands was initialized as a char *arrayOfArgs[]
Was it helpful?

Solution

First thing:

//if not last command
if(i < commands)

should be

if(i < commands -1)

since igoes from 0 to commands -1 that should solve dup2: Bad file descriptor

ls: |: No such file or directory

ls: wc: No such file or directory

are caused by malformed arrayOfCommands. It has to be initialized by

char * arrayOfCommands[] = {"ls", "-l", NULL};

and

char * arrayOfCommands[] = {"wc", NULL};

respectively and invoked via execvp(arrayOfCommands[0], arrayOfCommands)

essentially arrayOfCommands has to be of the same format that your argument vector (typically argv) of an int main(int argc, char** argv).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top