c - Linux 上带有管道和 I/O 重定向的 Shell 模拟器

标签 c linux shell redirect pipeline

我正在尝试在 Linux 上编写一个 shell 模拟器,它可以执行单进程命令、双进程管道和 I/O 重定向。 但是,当我使用以下正确执行单个进程时,管道存在一些问题。

Copy_STDIO();                                       //Copy the stdio for the restoration.
Redirection();                                      //Determine whether should do Redirection.
Pipeline(); 
Restore_stdio();                                    //Restore the stdio.

上面是我在 main 中的函数。首先我复制 STDIO 用于 I/O 重定向后的恢复。然后我将我的文件描述符复制到 STD_IN 和 STD_OUT。然后我执行管道,最后我恢复我的 STD_IN 和 STD_OUT。一切听起来都很完美,但实际上并非如此。我的 OUTPUT 重定向失败,这意味着当我的程序仍在运行时,它没有向目标文件写入任何内容(即,如果在单一情况下:ls > 123、123 不显示任何内容)。但是当我用exit(EXIT_SUCCESS)终止程序时,它出现了!!(如果用ctrl+c,它也失败了),我不知道为什么!

void Redirection()
{
    fd_out = open(filename[0],O_WRONLY | O_TRUNC | O_CREAT,S_IRWXU | S_IRWXG | S_IRWXO);    //O_WRONLY and O_CREAT must use at the same time.
    dup2(fd_out,STD_OUTPUT);
    close(fd_out);
}
void Copy_STDIO()
{
    STDIN_COPY = dup(STD_INPUT);                                //Copy for the stdin and stdout
    STDOUT_COPY = dup(STD_OUTPUT);  
}
void Pipeline()
{
if(pipeline)
{
    pipe(&fd[0]);
    childID=fork(); 

    if(childID==0)
    {

        if(fork()!=0)
        {
            close(fd[1]);
            dup2(fd[0],STD_INPUT);
            close(fd[0]);

            execvp(args2[0],args2);

        }
        else
        {
            close(fd[0]);
            dup2(fd[1],STD_OUTPUT);
            close(fd[1]);

            execvp(args[0],args);

        }

    }

    usleep(5000);                                               
}
}
void Restore_stdio()
{
    dup2(STDIN_COPY,STD_INPUT);                                 //Restore the output and input to the stdin and stdout.
    dup2(STDOUT_COPY,STD_OUTPUT);
    close(STDIN_COPY);
    close(STDOUT_COPY);
}

最佳答案

我在你自己的仓库上推送了两个提交,它们包含你需要的修复

void Pipelining()
{
   int otherID  = 0;
   childID = 0;
   int childs = 0;
   int ret = 0;
    if(pipeline)
    {
    pipe(fd);
    childID=fork();

       if (childID < 0) {
           fprintf(stderr, "error: 1 ... %s\n", strerror(errno));
           return;
       } else if (childID > 0) {
           ++childs;
           otherID = fork();
           if (otherID < 0) {
               fprintf(stderr, "error: 2 ... %s\n", strerror(errno));
               ret = waitpid(childID, NULL, 0);
               if (ret < 0) {
                   fprintf(stderr, "error: 3 ... %s\n", strerror(errno));
               }
               return;
           } else if (otherID > 0) {
               ++childs;
           } else {
            close(fd[0]);
            ret = dup2(fd[1],STD_OUTPUT);
               if (ret < 0) {
                   fprintf(stderr, "error: 4 ... %s\n", strerror(errno));
               }
            close(fd[1]);

            ret = execvp(args[0], args);
               if (ret < 0) {
                  fprintf(stderr, "error: 5 ... %s\n", strerror(errno));
               }
           }
       } else {
            close(fd[1]);
            ret = dup2(fd[0],STD_INPUT);
           if (ret < 0) {
               fprintf(stderr, "error: 6 ... %s\n", strerror(errno));
           }
            close(fd[0]);

            ret = execvp(args2[0],args2);
           if (ret < 0) {
               fprintf(stderr, "error: 7 ... %s\n", strerror(errno));
           }
       }
       /* now it is safe to close pipe */
       close(fd[0]);
       close(fd[1]);
       /* wait children */
       ret = waitpid(childID, NULL, 0);
       if (ret < 0) {
           if (errno != ECHILD)
               fprintf(stderr, "error: 8 ... %s\n", strerror(errno));
       }
       ret = waitpid(otherID, NULL, 0);
       if (ret < 0) {
           if (errno != ECHILD)
               fprintf(stderr, "error: 9 ... %s\n", strerror(errno));
       }
   }
}

我做了以下更改:

  1. 现在两个 child 都是从同一个顶级 parent 创建的
  2. 现在顶层父关闭他的管道文件描述符副本
  3. 现在顶层 parent 正确地等待它的 child
  4. 添加了急需的状态检查

关于c - Linux 上带有管道和 I/O 重定向的 Shell 模拟器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36113746/

相关文章:

c - 如何在C语言中乘以一个字符串?

linux - 如何找到空闲的 TCP 端口

linux - 如何在没有行号或 ASCII 表的情况下仅打印来自 hexdump 的十六进制值?

Linux - 在特定时间启动 recordmydestop

linux - 匹配模式并通过替换旧值来分配新值

c - 在C中添加不同类型的变量

c - GCC 避免将分支编译为链接寄存器 (blr) 语句

c++ - Simulink 和 DLL

linux - 为什么在下载 Linux 应用程序时需要选择我的处理器架构?

linux - 执行rm前提示文件列表