我正在尝试使用C为Linux创建一个基本shell。我已经让它工作,直到我尝试进行输出重定向,它只是破坏了一切。当我运行此代码时,它直接进入fork()的默认情况。我不知道为什么。如果我在子进程中摆脱for循环它可以工作,但即使使用for循环,我也不明白为什么甚至从未输入子进程。如果我在子进程的顶部放置一个print语句,它就不会被打印出来。
当我在命令行中运行它时,我得到提示并键入类似" ls"之类的东西,在我添加for循环之前有效,但现在我得到了#34;%am am i here& #34;如果我按下输入它只是继续给我相同的线。我的目标是能够键入" ls>输出"并让它工作。我认为输入重定向有效,但说实话,我甚至没有玩过它,因为我对输出重定向的情况非常困惑。任何帮助都将不胜感激,我花了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);
}
答案 0 :(得分:2)
我添加了一个单独的参数传递来捕获并记住I / O重定向,并将它们从传递给子节点的arg列表中删除。
这是更正后的代码[请原谅无偿风格的清理]:
#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条线?
是
我从我的另一个SO回答中借用了xstr
[带有错误修正和增强功能]。 dlk
是新的,但我做了很多,所以很容易。其中大部分是新代码。
但是......它由我以前做过的片段/概念组成:tgb,FWD,BTV,sysmagic。请注意struct foo
的所有结构成员都以foo_
[标准为我]为前缀。使用DLHDEF
和DLKDEF
模拟继承/模板的宏“技巧”也是我[必要时]做的事情。
许多函数变量重用我的样式:idx
用于索引var [我永远不会使用i/j
,而是xidx/yidx
],cp
用于char指针,{{1对于计数,cnt
表示字节长度等等。因此,我不必“思考”小东西[战术]并且可以专注于策略。
以上len
等。人。对我来说是一种“签名风格”。它不一定比其他人更好[或更糟]。它来自于当链接器/加载器只能处理8个字符符号时我开始使用C的事实,因此简洁是关键。但是,我习惯使用较短的名字。考虑哪个更清楚/更好:
idx
或者:
for (fooidx = 0; fooidx <= 10; ++fooidx)
我使用for (indexForFooArray = 0; indexForFooArray <= 10; ++indexForFooArray)
来避免do { ... } while (0)
阶梯很多。这称为“一次通过”循环。这被认为是“有争议的”,但是,根据我的经验,它使代码更清晰。就个人而言,我从未发现使用if/else
或do/while
循环 - YMMV无法更轻松/更好地完成while
循环的[更标准]使用。事实上,许多语言根本没有for
。
另外,我使用小写,除非它是do/while
[或#define
],它始终是高位的。也就是说,我使用“蛇案”(例如enum
)和不“驼峰案例”(例如fooidx
)。
自动生成包含函数原型的indexForFooArray
文件。这是巨大的节省时间。 旁注:确保您至少从外部链接获得v2,因为Makefile中存在错误。 .proto
会删除make clean
。 v2不会这样做
.proto
。
给定风格[一个人自己]的一致性是关键。对于给定的函数,您不必担心您将为变量命名的内容,您将使用的缩进或空行。
这有助于读者/新开发者。他们可以阅读一些函数,看看游戏中的风格,然后更快,因为所有功能都有相似的风格。
所有这些都可以让你“更快”,并在第一次尝试时仍能获得高质量的代码。我也非常经验丰富。
另外,我的代码评论侧重于“意图”。也就是说,您希望代码以现实世界的术语来做什么。他们应该回答“什么/为什么”,代码就是“如何”。