Pipes, dup2 and exec()

后端 未结 3 1018
无人及你
无人及你 2020-12-03 05:53

I have to write a shell that can run pipes. For example commands like ls -l | wc -l\". I have successfully parsed the command given by the user as below:

<
相关标签:
3条回答
  • 2020-12-03 06:35

    Hmm, close enough. You miss to handle close on some file descriptor after fork.

    Here is some reference:

    1. About pipe, http://unixwiz.net/techtips/remap-pipe-fds.html
    2. About file descriptor, http://www.usna.edu/Users/cs/aviv/classes/ic221/s14/lec/09/lec.html

    Here's my code:

    #include  <fcntl.h>                              //
    #include  <stdio.h>                              //
    #include  <stdlib.h>                             //
    #include  <string.h>                             //
    #include  <sys/types.h>                          //
    #include  <sys/wait.h>                           //
    #include  <sys/stat.h>                           //
    #include  <termios.h>                            //
    #include  <unistd.h>                             //
                                                     //
    #define INPUT_END 1                              // INPUT_END means where the pipe takes input
    #define OUTPUT_END 0                             // OUTPUT_END means where the pipe produces output
                                                     //
    int main(int argc, char* argv[])                 //
    {                                                //
        pid_t pid1;                                  // [STDIN -> terminal_input, STDOUT -> terminal_output]                       (of the parent process)
        pid_t pid2;                                  //
        int fd[2];                                   //
                                                     //
        pipe(fd);                                    // [STDIN -> terminal_input, STDOUT -> terminal_output, fd[0] -> pipe_input, fd[1] -> pipe_output]
        pid1 = fork();                               //
                                                     //
        if(pid1==0)                                  //
        {                                            // I am going to be the wc process (i.e. taking input from the pipe)
            close(fd[INPUT_END]);                    // [STDIN -> terminal_input, STDOUT -> terminal_output, fd[1] -> pipe_output] (of the WC process)
            dup2(fd[OUTPUT_END], STDIN_FILENO);      // [STDIN -> pipe_output, STDOUT -> terminal_output, fd[1] -> pipe_output]    (of the WC process)
            close(fd[OUTPUT_END]);                   // [STDIN -> pipe_output, STDOUT -> terminal_output]                          (of the WC process)
            execlp("wc", "wc", "-l",(char*) NULL);   //
        }                                            //
        else                                         //
        {                                            //
            pid2=fork();                             //
                                                     //
            if(pid2==0)                              //
            {                                        // I am going to be the ls process (i.e. producing output to the pipe)
                close(fd[OUTPUT_END]);               // [STDIN -> terminal_input, STDOUT -> terminal_output, fd[0] -> pipe_input] (of the ls process)
                dup2(fd[INPUT_END], STDOUT_FILENO);  // [STDIN -> terminal_input, STDOUT -> pipe_input, fd[0] -> pipe_input]      (of the ls process)
                close(fd[INPUT_END]);                // [STDIN -> terminal_input, STDOUT -> pipe_input]                           (of the ls process)
                execlp("ls","ls","-l",(char*) NULL); //
            }                                        //
                                                     //
            close(fd[OUTPUT_END]);                   // [STDIN -> terminal_input, STDOUT -> terminal_output, fd[0] -> pipe_input] (of the parent process)
            close(fd[INPUT_END]);                    // [STDIN -> terminal_input, STDOUT -> terminal_output]                      (of the parent process)
            waitpid(-1, NULL, 0);                    // As the parent process - we wait for a process to die (-1) means I don't care which one - it could be either ls or wc
            waitpid(-1, NULL, 0);                    // As the parent process - we wait for the another process to die.
                                                     // At this point we can safely assume both process are completed
        }                                            //
    }                                                //
    
    0 讨论(0)
  • 2020-12-03 06:40

    You need to close all the pipe descriptors in both the parent process and the child process (after duplication in the child process). In your code the main issue is that, the wc process does not exit because there are still writers present (since the parent process has not closed the write end). Changes shown below. I have also added the waitpid in the parent process to wait for the wc process.

    pid_t pid;
    int fd[2];
    
    pipe(fd);
    pid = fork();
    
    if(pid==0)
    {
        dup2(fd[WRITE_END], STDOUT_FILENO);
        close(fd[READ_END]);
        close(fd[WRITE_END]);
        execlp(firstcmd, firstcmd, frsarg, (char*) NULL);
        fprintf(stderr, "Failed to execute '%s'\n", firstcmd);
        exit(1);
    }
    else
    { 
        pid=fork();
    
        if(pid==0)
        {
            dup2(fd[READ_END], STDIN_FILENO);
            close(fd[WRITE_END]);
            close(fd[READ_END]);
            execlp(scmd, scmd, secarg,(char*) NULL);
            fprintf(stderr, "Failed to execute '%s'\n", scmd);
            exit(1);
        }
        else
        {
            int status;
            close(fd[READ_END]);
            close(fd[WRITE_END]);
            waitpid(pid, &status, 0);
        }
    }
    
    0 讨论(0)
  • 2020-12-03 06:47

    Well, a simple and efficient work arund for something like this is making a script with the pipe stuff and then calling the script with some exec command from your C code.

    script.sh

    #!/bin/sh
    ls -l | wc -l
    

    And the you just do in your C program something like this

    char *argv[] = {"script.sh", NULL};
    execv(argv[0], argv);
    

    Note that you'll have to have the script.sh in the same directory of your C program.

    0 讨论(0)
提交回复
热议问题