管道、dup2和exec()

管道、dup2和exec(),c,linux,shell,exec,dup2,C,Linux,Shell,Exec,Dup2,我必须编写一个可以运行管道的shell。例如像ls-l | wc-l”这样的命令。我成功地解析了用户给出的命令,如下所示: “ls”=firstcmd “-l”=新加坡联邦政府 “wc”=scmd “-l”=secarg 现在我必须使用两个fork,因为命令是两个和一个管道。 我为执行该命令而编写的代码块如下所示: pid_t pid; int fd[2]; pipe(fd); pid = fork(); if(pid==0) { dup2(fd[WRITE_END]

我必须编写一个可以运行管道的shell。例如像
ls-l | wc-l
”这样的命令。我成功地解析了用户给出的命令,如下所示:

“ls”=firstcmd

“-l”=新加坡联邦政府

“wc”=scmd

“-l”=secarg

现在我必须使用两个fork,因为命令是两个和一个管道。 我为执行该命令而编写的代码块如下所示:

pid_t pid;
int fd[2];

pipe(fd);
pid = fork();

if(pid==0)
{        
    dup2(fd[WRITE_END], STDOUT_FILENO);
    close(fd[READ_END]);
    execlp(firstcmd, firstcmd, frsarg, (char*) NULL);
}
else
{ 
    pid=fork();

    if(pid==0)
    {
        dup2(fd[READ_END], STDIN_FILENO);
        close(fd[WRITE_END]);
        execlp(scmd, scmd, secarg, (char*) NULL);
    }
}
因此,当我运行shell并输入命令
ls-l | wc-l
(例如)时,execs的结果不会显示,但shell会保持正常运行

奇怪的是,命令的结果只有在我用“exit”或“^C”终止shell时才会显示


该输出有什么问题?为什么在我输入命令后它不立即显示?

嗯,足够近了。在fork之后,您无法处理某些文件描述符上的close

以下是一些参考资料:

  • 关于管道
  • 关于文件描述符,
  • 这是我的密码:

    #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
        }                                            //
    }                                                //
    
    #包括//
    #包括//
    #包括//
    #包括//
    #包括//
    #包括//
    #包括//
    #包括//
    #包括//
    //
    #定义输入端1//INPUT\u END表示管道接收输入的位置
    #定义输出端0//OUTPUT\u END表示管道产生输出的位置
    //
    int main(int argc,char*argv[])//
    {                                                //
    pid_t pid1;//[STDIN->terminal_input,STDOUT->terminal_output](父进程的)
    pid_t pid2//
    int-fd[2]//
    //
    管道(fd);/[STDIN->terminal_input,STDOUT->terminal_output,fd[0]->pipe_input,fd[1]->pipe_output]
    pid1=fork()//
    //
    如果(pid1==0)//
    {//我将成为wc流程(即从管道中获取输入)
    关闭(fd[INPUT_END]);/[STDIN->terminal_INPUT,STDOUT->terminal_output,fd[1]->pipe_output](WC进程的)
    dup2(fd[OUTPUT\U END],STDIN\U FILENO);/[STDIN->pipe\U OUTPUT,STDOUT->terminal\U OUTPUT,fd[1]->pipe\U OUTPUT](WC过程的)
    关闭(fd[OUTPUT_END]);/[STDIN->pipe_OUTPUT,STDOUT->terminal_OUTPUT](WC进程的)
    execlp(“wc”、“wc”、“-l”、(char*)NULL)//
    }                                            //
    否则//
    {                                            //
    pid2=fork()//
    //
    如果(pid2==0)//
    {//我将成为ls进程(即,向管道生成输出)
    关闭(fd[OUTPUT_END]);/[STDIN->terminal_input,STDOUT->terminal_OUTPUT,fd[0]->pipe_input](ls进程的)
    dup2(fd[INPUT_END],标准输出文件号);/[STDIN->terminal_INPUT,标准输出->pipe_INPUT,fd[0]->pipe_INPUT](ls进程的)
    关闭(fd[INPUT_END]);/[STDIN->terminal_INPUT,STDOUT->pipe_INPUT](ls进程的)
    execlp(“ls”、“ls”、“-l”、(char*)NULL)//
    }                                        //
    //
    关闭(fd[OUTPUT_END]);/[STDIN->terminal_input,STDOUT->terminal_OUTPUT,fd[0]->pipe_input](父进程的)
    关闭(父进程的fd[INPUT_END]);//[STDIN->terminal_INPUT,STDOUT->terminal_output)
    waitpid(-1,NULL,0);//作为父进程-我们等待进程死亡(-1)意味着我不关心哪个进程-它可以是ls或wc
    waitpid(-1,NULL,0);//作为父进程-我们等待另一个进程死亡。
    //此时,我们可以安全地假设两个过程都已完成
    }                                            //
    }                                                //
    
    您需要关闭父进程和子进程中的所有管道描述符(在子进程中复制之后)。在您的代码中,主要问题是,
    wc
    进程没有退出,因为仍然存在写入程序(因为父进程没有关闭写入端)。更改如下所示。我还在父进程中添加了
    waitpid
    ,以等待
    wc
    进程

    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);
        }
    }
    

    好的,对于这样的事情,一个简单而有效的工作就是用管道制作一个脚本,然后用C代码中的某个exec命令调用该脚本

    script.sh

    #!/bin/sh
    ls -l | wc -l
    
    你在C程序中所做的就是这样

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

    请注意,您必须将script.sh放在C程序的同一目录中。

    您需要关闭父进程中的管道FD。您可以编辑我的代码以帮助我理解您的意思吗?@BarmarRule of thumb:如果您将管道的一端复制到标准输入或标准输出,您应该关闭原始管道的两端在使用
    exec*()
    函数(或继续)之前使用管道