使用C在Linux shell中实现input/输出redirect

我正在尝试使用C为Linux创build一个基本的shell。我已经得到它的工作,直到我尝试做输出redirect,它只是破坏了一切。 当我运行这个代码时,它直接转到fork()的默认情况。 我不知道为什么。 如果我摆脱了subprocess中的for循环它的工作原理,但即使使用for循环,我不明白为什么subprocess甚至从来没有进入。 如果我把一个打印语句放在subprocess的顶部,它不会被打印出来。

当我在命令行中运行这个命令时,我得到提示符并input类似于“ls”的内容,这在我添加for循环之前已经工作了,但现在我只是得到“%我在这里”,如果按Enter键,同一条线。 我的目标是能够input“ls>输出”并使其工作。 我认为inputredirect的作用,但老实说,我甚至没有玩过它,因为我很困惑,输出redirect正在发生什么。 任何帮助将不胜感激,我花了4个小时,像15线试图让这个工作。

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <signal.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/types.h> #include <sys/wait.h> #define HISTORY_COUNT 20 char *prompt = "% "; int main() { int pid; //int child_pid; char line[81]; char *token; char *separator = " \t\n"; char **args; char **args2; char **args3; char cmd[100]; char *hp; char *cp; char *ifile; char *ofile; int check; int pfds[2]; int i; int j; int current = 0; int p = 0; //int check; char *hist[HISTORY_COUNT]; //char history[90]; //typedef void (*sighandler_t) (int); args = malloc(80 * sizeof(char *)); args2 = malloc(80 * sizeof(char *)); signal(SIGINT, SIG_IGN); while (1) { fprintf(stderr, "%s", prompt); fflush(stderr); if (fgets(line, 80, stdin) == NULL) break; // split up the line i = 0; while (1) { token = strtok((i == 0) ? line : NULL, separator); if (token == NULL) break; args[i++] = token; /* build command array */ } args[i] = NULL; if (i == 0){ continue; } // assume no redirections ofile = NULL; ifile = NULL; // split off the redirections j = 0; i = 0; while (1) { //stackoverflow.com/questions/35569673 cp = args[i++]; if (cp == NULL) break; switch (*cp) { case '<': if (cp[1] == 0) cp = args[i++]; else ++cp; ifile = cp; break; case '>': if (cp[1] == 0) cp = args[i++]; else ++cp; ofile = cp; break; case '|': if(cp[1] ==0){ cp = args[i++]; if(pipe(pfds) == -1){ perror("Broken Pipe"); exit(1); } p = 1; } else{ ++cp; } break; default: args2[j++] = cp; args3[cp++] = cp break; } } args2[j] = NULL; if (j == 0) continue; switch (pid = fork()) { case 0: // open stdin if (ifile != NULL) { int fd = open(ifile, O_RDONLY); if (dup2(fd, STDIN_FILENO) == -1) { fprintf(stderr, "dup2 failed"); } close(fd); } // open stdout if (ofile != NULL) { // args[1] = NULL; int fd2; if ((fd2 = open(ofile, O_WRONLY | O_CREAT, 0644)) < 0) { perror("couldn't open output file."); exit(0); } // args+=2; printf("okay"); dup2(fd2, STDOUT_FILENO); close(fd2); } if(p == 1){ //from stackoverflow.com/questions/2784500 close(1); dup(pfds[1]); close(pfds[0]); execvp(args2[0], args2); break; } if(strcmp(args2[0], "cd") == 0){ //cd command if(args2[1] == NULL){ fprintf(stderr, "Expected argument"); } else{ check = chdir(args2[1]); if(check != 0){ fprintf(stderr,"%s",prompt); } } break; } execvp(args2[0], args2); /* child */ signal(SIGINT, SIG_DFL); fprintf(stderr, "ERROR %s no such program\n", line); exit(1); break; case -1: /* unlikely but possible if hit a limit */ fprintf(stderr, "ERROR can't create child process!\n"); break; default: //printf("am I here"); if(p==1){ close(0); dup(pfds[0]); close(pfds[1]); //execvp(); } wait(NULL); //waitpid(pid, 0, 0); } } exit(0); 

}

我添加了一个单独的参数传递来捕获并记住I / O重定向,并将其从传递给子级的参数列表中移除。

这是更正后的代码[请原谅无偿风格的清理]:

 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <signal.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/types.h> #include <sys/wait.h> char *prompt = "% "; int main() { int pid; //int child_pid; char line[81]; char *token; char *separator = " \t\n"; char **args; char **args2; char *cp; char *ifile; char *ofile; int i; int j; int err; //int check; //char history[90]; //typedef void (*sighandler_t) (int); args = malloc(80 * sizeof(char *)); args2 = malloc(80 * sizeof(char *)); //signal(SIGINT, SIG_IGN); while (1) { fprintf(stderr, "%s", prompt); fflush(stderr); if (fgets(line, 80, stdin) == NULL) break; // split up the line i = 0; while (1) { token = strtok((i == 0) ? line : NULL, separator); if (token == NULL) break; args[i++] = token; /* build command array */ } args[i] = NULL; if (i == 0) continue; // assume no redirections ofile = NULL; ifile = NULL; // split off the redirections j = 0; i = 0; err = 0; while (1) { cp = args[i++]; if (cp == NULL) break; switch (*cp) { case '<': if (cp[1] == 0) cp = args[i++]; else ++cp; ifile = cp; if (cp == NULL) err = 1; else if (cp[0] == 0) err = 1; break; case '>': if (cp[1] == 0) cp = args[i++]; else ++cp; ofile = cp; if (cp == NULL) err = 1; else if (cp[0] == 0) err = 1; break; default: args2[j++] = cp; break; } } args2[j] = NULL; // we got something like "cat <" if (err) continue; // no child arguments if (j == 0) continue; switch (pid = fork()) { case 0: // open stdin if (ifile != NULL) { int fd = open(ifile, O_RDONLY); if (dup2(fd, STDIN_FILENO) == -1) { fprintf(stderr, "dup2 failed"); } close(fd); } // trying to get this to work // NOTE: now it works :-) // open stdout if (ofile != NULL) { // args[1] = NULL; int fd2; //printf("PLEASE WORK"); if ((fd2 = open(ofile, O_WRONLY | O_CREAT, 0644)) < 0) { perror("couldn't open output file."); exit(0); } // args+=2; printf("okay"); dup2(fd2, STDOUT_FILENO); close(fd2); } execvp(args2[0], args2); /* child */ signal(SIGINT, SIG_DFL); fprintf(stderr, "ERROR %s no such program\n", line); exit(1); break; case -1: /* unlikely but possible if hit a limit */ fprintf(stderr, "ERROR can't create child process!\n"); break; default: //printf("am I here"); wait(NULL); //waitpid(pid, 0, 0); } } exit(0); } 

更新:

如果你还在身边,你认为你可以帮我创建一个管道?

当然。 张贴在这里太大了。 请参阅: http : //pastebin.com/Ny1w6pUh


哇,你创造了3300行吗?

是。

我从另一个我的答案借用xstr [错误修正和增强]。 dlk是新的,但我做了很多,所以很容易。 大部分是新代码。

但是……它是由我以前做过的片段/概念组成的:tgb,FWD,BTV,sysmagic。 注意struct foo所有struct成员的前缀都是foo_ [standard for me]。 使用DLHDEFDLKDEF来模拟继承/模板的宏“诡计”也是我[必要时]做的事情。

许多函数vars重用我的风格: idx的索引var [我永远不会使用i/j ,而是xidx/yidx ], cp为char指针, cnt为count, len为字节长度等。因此,我没有“思考”小事(战术),可以专注于战略。

上面的idx et。 人。 是我的“签名风格”。 不一定比其他人更好(或更差)。 它来自于我开始使用C时,链接器/加载器只能处理8个字符的符号,所以简洁是关键。 但是,我习惯使用较短的名字。 考虑哪些更清楚/更好:

 for (fooidx = 0; fooidx <= 10; ++fooidx) 

要么:

 for (indexForFooArray = 0; indexForFooArray <= 10; ++indexForFooArray) 

我使用do { ... } while (0)来避免if/else梯子很多 。 这被称为“一次通过”循环。 这被认为是“有争议的”,但根据我的经验,这使代码更清洁。 就个人而言,我从来没有找到一个[更标准的]使用do/while循环,一段while或循环不能更容易/更好地完成 – YMMV。 事实上,许多语言甚至都没有do/while完全。

另外,我使用小写,除非是#define [或enum ],它总是上层。 也就是说,我使用“蛇案”(例如fooidx )而不是 “骆驼驼峰案”(例如indexForFooArray )。

包含函数原型的.proto文件是自动生成的。 这是一个巨大的节省时间。 附注:确保你至少有v2的外部链接,因为在Makefile中有一个错误。 make clean会清除.proto 。 v2不会这样做

多年来我开发了自己的风格。 原来linux内核风格是“从我的借用”。 实际上并不是:-)矿是第一位的。 但是…他们同时提出了一个和我的匹配的东西: /usr/src/kernels/whatever_version/Documentation/CodingStyle

一个给定的风格(自己的)的一致性是关键。 对于一个给定的函数,你不必担心你会命名变量,你将使用什么缩进或空行。

这有助于读者/新开发者。 他们可以阅读一些功能,看看正在使用的风格,然后加快速度,因为所有功能都有相似的风格。

所有这一切都可以让你“走得更快”,并在第一次尝试时仍能获得高质量的代码。 我也很有经验

另外,我的代码注释集中在“意图”上。 也就是说,你想要代码在现实世界中做什么。 他们应该回答“什么/为什么”,代码是“如何”。