首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >管道,dup2和exec()

管道,dup2和exec()
EN

Stack Overflow用户
提问于 2015-11-24 02:16:56
回答 3查看 73.3K关注 0票数 13

我得写一个能运行管道的外壳。例如,像ls -l | wc -l这样的命令。我成功地解析了用户发出的命令如下:

"ls“= firstcmd "-l“= frsarg "wc“= scmd "-l“= secarg

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

代码语言:javascript
复制
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时,命令的结果才会显示出来。

这个输出有什么问题?为什么在我输入命令后它就不出现?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-11-24 03:26:14

您需要同时关闭父进程和子进程中的所有管道描述符(在子进程复制之后)。在您的代码中,主要问题是,由于仍然存在写入器(因为父进程没有关闭写入端),wc进程不会退出。更改如下。我还在父进程中添加了waitpid,以等待wc进程。

代码语言:javascript
复制
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);
    }
}
票数 23
EN

Stack Overflow用户

发布于 2015-11-24 03:06:40

嗯,够近了。您错过了处理一个接一个的文件描述符的错误。

以下是一些参考资料:

  1. 关于管道,http://unixwiz.net/techtips/remap-pipe-fds.html
  2. 关于文件描述符,http://www.usna.edu/Users/cs/aviv/classes/ic221/s14/lec/09/lec.html

这是我的密码:

代码语言:javascript
复制
#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
    }                                            //
}                                                //
票数 2
EN

Stack Overflow用户

发布于 2020-03-17 21:00:21

好的,一个简单而高效的工作就是用管道来创建一个脚本,然后用C代码中的exec命令调用脚本。

script.sh

代码语言:javascript
复制
#!/bin/sh
ls -l | wc -l

你在你的C程序中所做的事情就像这样

代码语言:javascript
复制
char *argv[] = {"script.sh", NULL};
execv(argv[0], argv);

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

票数 -4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33884291

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档