【问题标题】:Why isn't my piped grep working?为什么我的管道 grep 不起作用?
【发布时间】:2014-04-23 07:44:04
【问题描述】:

我已经用 C 编写了自己的 shell,当我运行 ls | grep .c 时,我什么也得不到。尽管非管道命令运行良好,例如ls。这是我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

#include "shell.h"
#include "builtins.h"
#include "makeargv.h"



void shell() 
{
    pid_t   shell_pid;
    int     i;
    int     flag = 1;
    int     argc0;
    int     argc1;
    int     fdl[2];
    int     fdr[2];
    size_t  input_size;
    char    cwd[128];   //this is being toggled
    char    *delim0;
    char    *delim1;
    char    *lastarg;
    char    *input;
    char    *debugdescriptor;
    char    **argvp;
    char    **firstargs;

    shell_pid = getpid();
    do
    {
        // Retrieve PID & CWD of the parent process. 
        getcwd(cwd, (128 * sizeof(char)));  
        printf("{%i}%s$ ", shell_pid, cwd);         

        // Retrieve input from stdin.
        input = NULL;
        input_size = 0;
        getline(&input, &input_size, stdin);

        //seperates the input into pipe-delimited arguments("tokens")
        delim1 = "|\n";
        argc1 = makeargv(input, delim1, &argvp);

        //got some debugging tools here
        //debugdescriptor = "PIPE-SEPERATED";
        //debug_args(&argvp, &argc1, debugdescriptor);


        //check for quit and cd first
        delim0 = " ";
        argc0 = makeargv(argvp[0], delim0, &firstargs);
        //more debugging tools here
        //debugdescriptor = "FIRST ARGS";
        //debug_args(&firstargs, &argc0, debugdescriptor);
        //exit
        if((i = strcmp(firstargs[0],"exit")) == 0 || (i = strcmp(firstargs[0],"quit")) == 0)
        {
            printf("===========SHELL TERMINATED==============\n\n");
            flag = 0;
        }
        //cd
        else if((i = strcmp(firstargs[0],"cd")) == 0)
        {
            chdir(firstargs[1]);
        }

        else // Create a child process to handle user input.
        {
            char **thisarg;
            int childlayer = 0;
            pid_t pid = fork();
            wait(0);
            if(pid == 0)
                childlayer++;               
            int tokens = argc1 - 1;
            if(argc1 == 1 && pid == 0)
            {
                makeargv(argvp[tokens], delim0, &thisarg);
                execvp(thisarg[0], thisarg);
            }
            else //more than 1 arguement, (has pipes)
            {
                while(pid == 0 && childlayer < argc1){
                    if(childlayer == 1){ //rightmost
                        pipe(fdl);
                        pid = fork();
                        wait(0);
                        if(pid == 0)
                            childlayer++;
                        if(pid > 0){
                            close(fdl[1]);
                            dup2(fdl[0], STDIN_FILENO); //sets the final output to write to STDIN
                            execute(childlayer, argc1, &argvp);
                        }
                    }
                    else if(childlayer > 1 && childlayer < argc1-1){ //middle args
                        pipe(fdr);
                        fdr[1] = fdl[1];
                        fdr[0] = fdl[0];

                        dup2(fdr[1], STDOUT_FILENO);

                        pipe(fdl);
                        pid = fork();
                        wait(0);
                        if(pid == 0)
                            childlayer++;
                        if(pid > 0){
                            close(fdl[1]);
                            dup2(fdl[0], STDIN_FILENO);
                            execute(childlayer, argc1, &argvp);
                        }
                    }
                    else{ //leftmost
                        pipe(fdr);
                        fdr[0] = fdl[0];
                        fdr[1] = fdl[1];
                        close(fdr[0]);
                        dup2(fdr[1], STDOUT_FILENO);
                        execute(childlayer, argc1, &argvp);
                    }
                }       
            }
        }
    }while(flag == 1);  
}

我想我在使用管道时可能会卡在子进程中,但我无法看到在哪里。谢谢。

【问题讨论】:

  • 首先它应该是ls | grep \.c,否则它会匹配一个名为helloc的文件,你应该将它设为ls | grep \.c$,这样它就不会匹配hello.c.backup
  • 此类问题几乎总是由于未适当close()ing 管道。如果一个进程的管道两端都打开并且正在等待该管道上的 EOF,它永远不会到来(管道等待最后一个写入器首先关闭管道)。
  • @PlasmaPower : 为什么它应该是 \.c?反斜杠是点的转义字符吗?请详细说明您的意思,我有兴趣进一步了解。
  • grep 会将点视为通配符,除非它被反斜杠转义。需要美元来锚定正则表达式,以便它不会匹配文件名中间的 .c
  • “锚定正则表达式”我认为您的意思是它必须单独存在?所以例如 .csv 不匹配?

标签: c shell pipe stdin


【解决方案1】:

为什么你在fork() 之后紧接着wait(0)?在子进程中,这将立即返回并出现错误,但在父进程中,它将阻塞,直到子进程退出。我很难理解管道是如何建立的,因为每个孩子都在分叉管道中的下一个孩子。我猜wait(0) 正在制造一个先有鸡还是先有蛋的问题。直到孩子退出,父母才能开始,但孩子不能退出,因为它需要父母的输入。如果 shell 进程只是在管道组件上循环并自己分叉每个组件,然后等待它们全部完成,那不是更简单吗?

【讨论】:

  • 我使用 wait(0) 以便父级等待子级执行完毕后再继续。该外壳应该能够处理无限数量的管道。我的管道是如何工作的,假设我有“cmd1 | cmd2 | cmd 3 | cmd 4”,然后它将每个命令分叉一次,最低的孩子将处理 cmd1,使用 execvp 将 cmd1 的输出写入父级的 STDIN,这将结束子进程。然后紧随其后,等待的父级可以执行列表中的下一个命令(cmd2)。在任何分叉完成之前,这些命令都是用管道分隔的,所以孩子们可以看到它们。
  • 所以,一旦我们进入while(pid == 0 &amp;&amp; childlayer &lt; argc1)循环,第一次迭代进入childlayer == 1 case,它分叉,等待孩子退出,然后执行最右边的命令,从管道输入。这意味着子进程的所有输出都必须在管道中缓冲。但是如果孩子因为管道满了而背压,它不会退出,所以父母会陷入死锁。
  • 您仍然可以通过让 shell 进程在 argvp 数组上循环、创建管道和分叉子代而不调用 wait 来创建具有无限数量的命令的管道。只有当所有的孩子都被分叉时,shell 进程才应该调用wait 的次数与孩子的数量一样多。在普通 shell 中,管道中的所有子进程都将 shell 作为其父进程,而不是管道中它们之后的命令。
  • 我可以看到这将如何工作得更好。作为后续问题:当您让父进程循环并为每个管道分隔的命令创建一个子进程时,您如何确定它们将以正确的顺序完成?
  • 你为什么关心他们完成的顺序?
【解决方案2】:

您几乎可以肯定没有关闭所有文件描述符。此类错误的一个来源是您的dup2 调用。

之后:

dup2(fdr[1], STDOUT_FILENO);

你应该打电话

close(fdr[1]);

【讨论】:

  • 哦。这很有帮助。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-28
  • 2016-08-25
  • 1970-01-01
相关资源
最近更新 更多