实施管道以模仿壳牌

时间:2016-03-18 15:39:42

标签: c linux bash shell pipe

阅读器。我正在编写一个模仿linux shell的C程序。在实现管道之前,我的代码正常工作,并且在输出/输入文件上。接下来是实现诸如(a | b)| c的命令管道。以下是我的代码:

测试它,我得到“sfhjdj”,“exit”和“cd”的正确返回。我遇到的问题是运行一个简单的命令会返回ls: write error: Bad file descriptor。尝试管道只运行第一个功能。是什么导致这个?正如我从其他问题中看到的那样,我试图管道。

下面是我在实现管道之前的代码,我找不到错误,但可能是因为关闭/重复。感谢您阅读本文!

仅在执行功能中进行了更改。

编辑:帮助程序代码,prase.c

/*
 * parse.c - feeble command parsing for the Feeble SHell.
 */

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "parse.h"
#include "error.h"


#define MAXARGV 1000

enum token {
    identifier, directin, directout, doubledirectout,
    /* everything >= semicolon ends an individual "struct pipeline" */
    semicolon,
    ampersand,
    verticalbar, doubleampersand, doublebar, doublepipe,
    eol
};
static enum token gettoken(char **s, char **argp);
static char *ptok(enum token tok);


struct parsed_line *parse(char *s)
{
    struct parsed_line *retval;  /* remains freeparse()able at all times */
    struct parsed_line *curline;
    struct pipeline **plp;  /* where to append for '|' and '|||' */
    char *argv[MAXARGV];
    enum token tok;
    int argc = 0;
    int isdouble = 0;

    retval = curline = emalloc(sizeof(struct parsed_line));
    curline->conntype = CONN_SEQ;  /* i.e. always do this first command */
    curline->inputfile = curline->outputfile = NULL;
    curline->output_is_double = 0;
    curline->isbg = 0;
    curline->pl = NULL;
    curline->next = NULL;
    plp = &(curline->pl);

    do {
        if (argc >= MAXARGV)
            fatal("argv limit exceeded");
        while ((tok = gettoken(&s, &argv[argc])) < semicolon) {
            switch ((int)tok) {  /* cast prevents stupid warning message about
                                  * not handling all enum token values */
            case identifier:
                argc++;  /* it's already in argv[argc];
                          * increment to represent a save */
                break;
            case directin:
                if (curline->inputfile) {
                    fprintf(stderr,
                            "syntax error: multiple input redirections\n");
                    freeparse(curline);
                    return(NULL);
                }
                if (gettoken(&s, &curline->inputfile) != identifier) {
                    fprintf(stderr, "syntax error in input redirection\n");
                    freeparse(curline);
                    return(NULL);
                }
                break;
            case doubledirectout:
                curline->output_is_double = 1;
                /* fall through */
            case directout:
                if (curline->outputfile) {
                    fprintf(stderr,
                            "syntax error: multiple output redirections\n");
                    freeparse(curline);
                    return(NULL);
                }
                if (gettoken(&s, &curline->outputfile) != identifier) {
                    fprintf(stderr, "syntax error in output redirection\n");
                    freeparse(curline);
                    return(NULL);
                }
                break;
            }
        }

        /* cons up just-parsed pipeline component */
        if (argc) {
            *plp = emalloc(sizeof(struct pipeline));
            (*plp)->next = NULL;
            (*plp)->argv = eargvsave(argv, argc);
            (*plp)->isdouble = isdouble;
            plp = &((*plp)->next);
            isdouble = 0;
            argc = 0;
        } else if (tok != eol) {
            fprintf(stderr, "syntax error: null command before `%s'\n",
                    ptok(tok));
            freeparse(curline);
            return(NULL);
        }

        /* ampersanded? */
        if (tok == ampersand)
            curline->isbg = 1;

        /* is this a funny kind of pipe (to the right)? */
        if (tok == doublepipe)
            isdouble = 1;

        /* does this start a new struct parsed_line? */
        if (tok == semicolon || tok == ampersand || tok == doubleampersand || tok == doublebar) {
            curline->next = emalloc(sizeof(struct parsed_line));
            curline = curline->next;

            curline->conntype =
                (tok == semicolon || tok == ampersand) ? CONN_SEQ
                : (tok == doubleampersand) ? CONN_AND
                : CONN_OR;
            curline->inputfile = curline->outputfile = NULL;
            curline->output_is_double = 0;
            curline->isbg = 0;
            curline->pl = NULL;
            curline->next = NULL;
            plp = &(curline->pl);
        }

    } while (tok != eol);
    return(retval);
}


/* (*s) is advanced as we scan; *argp is set iff retval == identifier */
static enum token gettoken(char **s, char **argp)
{
    char *p;

    while (**s && isascii(**s) && isspace(**s))
        (*s)++;
    switch (**s) {
    case '\0':
        return(eol);
    case '<':
        (*s)++;
        return(directin);
    case '>':
        (*s)++;
        if (**s == '&') {
            (*s)++;
            return(doubledirectout);
        }
        return(directout);
    case ';':
        (*s)++;
        return(semicolon);
    case '|':
        if ((*s)[1] == '|') {
            *s += 2;
            return(doublebar);
        }
        (*s)++;
        if (**s == '&') {
            (*s)++;
            return(doublepipe);
        }
        return(verticalbar);
    case '&':
        if ((*s)[1] == '&') {
            *s += 2;
            return(doubleampersand);
        } else {
            (*s)++;
            return(ampersand);
        }
    /* else identifier */
    }

    /* it's an identifier */
    /* find the beginning and end of the identifier */
    p = *s;
    while (**s && isascii(**s) && !isspace(**s) && !strchr("<>;&|", **s))
        (*s)++;
    *argp = estrsavelen(p, *s - p);
    return(identifier);
}


static char *ptok(enum token tok)
{
    switch (tok) {
    case directin:
        return("<");
    case directout:
        return(">");
    case semicolon:
        return(";");
    case verticalbar:
        return("|");
    case ampersand:
        return("&");
    case doubleampersand:
        return("&&");
    case doublebar:
        return("||");
    case doubledirectout:
        return(">&");
    case doublepipe:
        return("|&");
    case eol:
        return("end of line");
    default:
        return(NULL);
    }
}


static void freepipeline(struct pipeline *pl)
{
    if (pl) {
        char **p;
        for (p = pl->argv; *p; p++)
            free(*p);
        free(pl->argv);
        freepipeline(pl->next);
        free(pl);
    }
}


void freeparse(struct parsed_line *p)
{
    if (p) {
        freeparse(p->next);
        if (p->inputfile)
            free(p->inputfile);
        if (p->outputfile)
            free(p->outputfile);
        freepipeline(p->pl);
        free(p);
    }

Builtin.c - 用于cd并退出

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "fsh.h"
#include "builtin.h"


int builtin_exit(char **argv)
{
    if (argv[1] && argv[2]) /* i.e. argc >= 2 */ {
        fprintf(stderr, "usage: exit [status]\n");
        fflush(stderr);
        return(1);
    } else if (argv[1]) {
        /* "exit ###" */
        exit(atoi(argv[1]));
    } else {
        /* "exit" with no argument */
        exit(laststatus);
    }
}

int builtin_cd(char **argv)
{
    if (argv[1] && argv[2]) {
        fprintf(stderr, "usage: %s dir\n", argv[0]);
        return(1);
    } else if (argv[1]) {
        chdir(argv[1]);
    } else {
        chdir(getenv("HOME"));
    }
    //if (chdir(argv[1])) {
    //    perror(argv[1]);
    //    return(1);
    //}
    return(0);
}

Parse.h - 管道是怎样的

enum connenum {
    CONN_SEQ,  /* sequential commands, i.e. separated by a semicolon */
    CONN_AND,  /* commands joined by '&&' */
    CONN_OR    /* commands joined by '||' */
};

struct pipeline {  /* list of '|'-connected commands */
    char **argv;  /* array ending with NULL */
    struct pipeline *next;  /* NULL if this doesn't pipe into anything */
    int isdouble; /* 1 if we have '|&' i.e. should dup onto 2 */
};

struct parsed_line { /* list of ';' or '&&' or '||' -connected struct pipelines */
    enum connenum conntype;  /* will be CONN_SEQ if this is the first item */
    char *inputfile, *outputfile;  /* NULL for no redirection */
    int output_is_double;  /* output redirection is '>&' rather than '>' */
    struct pipeline *pl;  /* the command(s) */
    int isbg;  /* non-zero iff there is a '&' after this command */
    struct parsed_line *next;   /* connected as specified by next->conntype */
};


extern struct parsed_line *parse(char *s);
extern void freeparse(struct parsed_line *p);

1 个答案:

答案 0 :(得分:1)

参考 Creating Pipes in C

参考 GNU Pipe to a Subprocess

如果你真的想要了解煤炭,可以帮助你。

如果您更喜欢使用 popen

,这可能对您有所帮助

参考 Pipes the Easy Way using popen

我测试了这个,它可以做你想要的事情例如打开2个管道(ls和sort命令)注意 popen中的r属性(&#34; ls&#34;,&#34; r&#34;)我怀疑可能是上述代码的问题。

例如

  

如果父级想要接收来自孩子的数据,则关闭fd1 ,并且孩子关闭fd0 。如果父级要向子级发送数据,则关闭fd0 ,子级应关闭fd1

     

由于父母和孩子之间共享描述符,我们应该始终确保关闭我们并不关心的管道末尾。从技术角度来说,如果未明确关闭管道的不必要的末端,则永远不会返回EOF。

管道的文件描述符可能未设置为读取 / 写入或文件描述符按正确的顺序关闭..

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    FILE *pipein_fp, *pipeout_fp;
    char readbuf[80];

    /* Create one way pipe line with call to popen() */
    if (( pipein_fp = popen("ls", "r")) == NULL)
    {
            perror("popen");
            exit(1);
    }

    /* Create one way pipe line with call to popen() */
    if (( pipeout_fp = popen("sort", "w")) == NULL)
    {
            perror("popen");
            exit(1);
    }

    /* Processing loop */
    while(fgets(readbuf, 80, pipein_fp))
            fputs(readbuf, pipeout_fp);

    /* Close the pipes */
    pclose(pipein_fp);
    pclose(pipeout_fp);

    return(0);
}

如果您需要Parsing Program Arguments

Getopt:使用getopt解析程序选项。

Argp:使用argp_parse解析程序选项。

子选项:某些程序需要更详细的选项。

可以帮助你。

一切顺利