簡體   English   中英

實施管道以模仿殼牌

[英]Implementing piping to mimic Shell

讀者。 我正在編寫一個模仿 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);

參考在 C 中創建管道

引用GNU 管道到子進程

如果你真的想深入到煤層,可能會幫助你。

如果您更喜歡使用popen,這可能會對您有所幫助

Ref Pipes 使用 popen 的簡單方法

我已經對此進行了測試,它可以執行您想要的操作 例如打開 2 個管道(ls 和 sort 命令) 注意popen("ls", "r")中的 r 屬性,我懷疑這可能是上述代碼的問題。

例如

如果父級想要從子級接收數據,它應該關閉 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);
}

如果您需要解析程序參數

Getopt:使用getopt 解析程序選項。

Argp:使用argp_parse 解析程序選項。

• 子選項:某些程序需要更詳細的選項。

可以幫到你。

祝一切順利

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM