【发布时间】:2016-03-18 15:39:42
【问题描述】:
读者。我正在编写一个模仿 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);
【问题讨论】:
-
代码无法编译。如果您发布minimal reproducible example,您可能可以获得更多帮助。
-
@terencehill 该程序有多个帮助文件,虽然执行函数本身用的不是最多。我已经用更多代码更新了帖子。
-
这不完全是 mcve。您应该尝试制作一个重现错误的最小示例。
-
我会,但我担心它依赖很多,一个最小的例子是无法访问的。唯一能让我关注的方法是提到
execute中发生的变化,我在其中引入了管道元素,例如pipe()、dup2()和内部fork()。