简体   繁体   English

实施管道以模仿壳牌

[英]Implementing piping to mimic Shell

reader.读者。 I'm writing a C program that mimics the linux shell.我正在编写一个模仿 linux shell 的 C 程序。 Before implementing piping, my code worked normally, and on output/input files.在实施管道之前,我的代码正常工作,并且在输出/输入文件上。 Next was implementing command piping such as (a|b)|c.接下来是实现命令管道,例如 (a|b)|c。 Below is my code:下面是我的代码:

Testing it, I get the proper returns for "sfhjdj", "exit", and "cd".测试它,我得到了“sfhjdj”、“exit”和“cd”的正确回报。 The issue that I have is that running a simple command returns ls: write error: Bad file descriptor .我遇到的问题是运行一个简单的命令会返回ls: write error: Bad file descriptor Attempting to pipe only runs the first function as well.尝试管道也只运行第一个函数。 Any idea on what's causing this?关于是什么原因造成的任何想法? I attempted to pipe as I've seen from other questions here.正如我从这里的其他问题中看到的那样,我试图进行管道传输。

Below is my code before implementing piping, I can't find the error but it might be because of closing/duping.下面是我在实现管道之前的代码,我找不到错误,但这可能是因为关闭/重复。 Thank you for reading this!谢谢您阅读此篇!

Only change was in the execute function.唯一的变化是在执行函数中。

EDIT : Helper code, prase.c编辑:助手代码,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 - for cd and exit 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 - how the pipeline is 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);

Ref Creating Pipes in C参考在 C 中创建管道

Ref GNU Pipe to a Subprocess引用GNU 管道到子进程

May help you if you really want to get down to the coal face.如果你真的想深入到煤层,可能会帮助你。

This may help you if you prefer to use popen如果您更喜欢使用popen,这可能会对您有所帮助

Ref Pipes the Easy Way using popen Ref Pipes 使用 popen 的简单方法

I've test this and it does what you want Eg opens 2 pipes (The ls and sort command) Notice the r attribute in popen("ls", "r") which i suspect may have been the issue with the above code.我已经对此进行了测试,它可以执行您想要的操作 例如打开 2 个管道(ls 和 sort 命令) 注意popen("ls", "r")中的 r 属性,我怀疑这可能是上述代码的问题。

Eg例如

If the parent wants to receive data from the child , it should close fd1 , and the child should close fd0 .如果父级想要从子级接收数据,它应该关闭 fd1 ,子级应该关闭 fd0 If the parent wants to send data to the child , it should close fd0 , and the child should close fd1 .如果父级想要向子级发送数据,它应该关闭 fd0子级应该关闭 fd1

Since descriptors are shared between the parent and child, we should always be sure to close the end of pipe we aren't concerned with.由于描述符在父级和子级之间共享,我们应该始终确保关闭我们不关心的管道末端。 On a technical note, the EOF will never be returned if the unnecessary ends of the pipe are not explicitly closed.从技术上讲,如果管道的不必要端没有明确关闭,则永远不会返回 EOF。

The file descriptor for the pipe may not have been set to Read / Write or the File descriptors closed in the right order..管道的文件描述符可能未设置为读取/写入或文件描述符以正确的顺序关闭。

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

If you need to Parsing Program Arguments如果您需要解析程序参数

Getopt: Parsing program options using getopt. Getopt:使用getopt 解析程序选项。

Argp: Parsing program options using argp_parse. Argp:使用argp_parse 解析程序选项。

Suboptions: Some programs need more detailed options. • 子选项:某些程序需要更详细的选项。

May help you.可以帮到你。

All the best祝一切顺利

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM