[英]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);
如果你真的想深入到煤層,可能會幫助你。
如果您更喜歡使用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.