Implementing shell in C and need help handling input/output redirection Implementing shell in C and need help handling input/output redirection shell shell

Implementing shell in C and need help handling input/output redirection


You have way too many file descriptors open after your redirection. Let's dissect the two paragraphs:

if (in) { //if '<' char was found in string inputted by user    fd = open(input, O_RDONLY, 0);    dup2(fd, STDIN_FILENO);    in = 0;    current_in = dup(0);  // Fix for symmetry with second paragraph}if (out) { //if '>' was found in string inputted by user    fd = creat(output, 0644);    dup2(fd, STDOUT_FILENO);    out = 0;    current_out = dup(1);}

I'm going to be charitable and ignore the fact that you are ignoring errors. However, you will need to error check your system calls.

In the first paragraph, you open a file and capture the file descriptor (it might well be 3) in the variable fd. You then duplicate the file descriptor over standard input (STDIN_FILENO). Note, though, that file descriptor 3 is still open. Then you do a dup(0) (which, for consistency, should be STDIN_FILENO), getting another file descriptor, perhaps 4. So you have file descriptors 0, 3 and 4 pointing at the same file (and, indeed, the same open file description — noting that an open file description is different from an open file descriptor). If your intention with current_in was to preserve the (parent) shell's standard input, you have to do that dup() before you do the dup2() that overwrites the output. However, you would be better off not altering the parent shell's file descriptors; it is less overhead than re-duplicating the file descriptors.

Then you more or less repeat the process in the second paragraph, first overwriting the only record of file descriptor 3 being open with the fd = creat(...) call but obtaining a new descriptor, perhaps 5, then duplicating that over standard output. You then do a dup(1), yielding another file descriptor, perhaps 6.

So, you have stdin and stdout of the main shell redirected to the files (and no way of reinstating those to the original values). Your first problem, therefore, is that you are doing the redirection before you fork(); you should be doing it after the fork() — though when you get to piping between processes, you will need to create pipes before forking.

Your second problem is that you need to close a plethora of file descriptors, one of which you no longer have a reference for.

So, you might need:

if ((pid = fork()) < 0)    ...error...else if (pid == 0){    /* Be childish */    if (in)    {        int fd0 = open(input, O_RDONLY);        dup2(fd0, STDIN_FILENO);        close(fd0);    }    if (out)    {        int fd1 = creat(output , 0644) ;        dup2(fd1, STDOUT_FILENO);        close(fd1);    }    ...now the child has stdin coming from the input file,     ...stdout going to the output file, and no extra files open.    ...it is safe to execute the command to be executed.    execve(cmd[0], cmd, env);   // Or your preferred alternative    fprintf(stderr, "Failed to exec %s\n", cmd[0]);    exit(1);}else{    /* Be parental */    ...wait for child to die, etc...}

Before you do any of this, you should ensure that you've already flushed the shell's standard I/O channels, probably by using fflush(0), so that if the forked child writes to standard error because of a problem, there is no extraneous duplicated output.

Also note that the various open() calls should be error-checked.


You have way too many file descriptors open after your redirection. The code which you need is this.

    if (pid == 0){          /* for the child process:         */    // function for redirection ( '<' , '>' )    int fd0,fd1,i,in=0,out=0;    char input[64],output[64];    // finds where '<' or '>' occurs and make that argv[i] = NULL , to ensure that command wont't read that    for(i=0;argv[i]!='\0';i++)    {        if(strcmp(argv[i],"<")==0)        {                    argv[i]=NULL;            strcpy(input,argv[i+1]);            in=2;                   }                       if(strcmp(argv[i],">")==0)        {                  argv[i]=NULL;            strcpy(output,argv[i+1]);            out=2;        }             }    //if '<' char was found in string inputted by user    if(in)    {           // fdo is file-descriptor        int fd0;        if ((fd0 = open(input, O_RDONLY, 0)) < 0) {            perror("Couldn't open input file");            exit(0);        }                   // dup2() copies content of fdo in input of preceeding file        dup2(fd0, 0); // STDIN_FILENO here can be replaced by 0         close(fd0); // necessary    }    //if '>' char was found in string inputted by user     if (out)    {        int fd1 ;        if ((fd1 = creat(output , 0644)) < 0) {            perror("Couldn't open the output file");            exit(0);        }                   dup2(fd1, STDOUT_FILENO); // 1 here can be replaced by STDOUT_FILENO        close(fd1);    }    execvp(*argv, argv);    perror("execvp");    _exit(1);    // another syntax    /*      if (!(execvp(*argv, argv) >= 0)) {     // execute the command              printf("*** ERROR: exec failed\n");            exit(1);     */ }    else if((pid) < 0)    {             printf("fork() failed!\n");        exit(1);    }    else {                                  /* for the parent:      */        while (!(wait(&status) == pid)) ; // good coding to avoid race_conditions(errors)     }}


Here's what's happening. After you call fork() there are two processes executing that are duplicates of the original process. The difference is in the return value of fork() which is stored in pid.

Then both processes (the shell and the child) redirect their stdin and stdout to the same files. I think you were trying to save the previous fd in current_out, but as Seth Robertson points out, this doesn't currently work, since the wrong file descriptor is being saved. The parent also restores its stdout, but not stdin.

You could fix this bug, but you can do better. You don't actually have to redirect parent's output, just the child's. So simply check pid first. Then there is also no need to restore any file descriptors.