使用C在Linux Shell中实现输入/输出重定向

我正在尝试使用C为Linux创建一个基本的shell。在我尝试进行输出重定向之前,它已经可以工作了,并且它破坏了一切。当我运行此代码时,它直接进入fork()的默认情况。我不知道为什么。如果我在子进程中摆脱了for循环,那么它就可以工作,但是即使有了for循环,我也不明白为什么子进程甚至从未输入。如果将打印语句放在子进程的顶部,则不会打印出来。

当我在命令行中运行此命令时,我得到提示并键入“ ls”之类的内容,在添加for循环之前它可以正常工作,但是现在我仅获得“%am i

here”,如果按回车,它将一直给我同一条线。我的目标是能够键入“

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);

}

回答:

我添加了一个单独的参数传递来捕获和记住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条线吗?

是。

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

但是…它由我之前做过的片段/概念组成:tgb,FWD,BTV,sysmagic。请注意,所有struct成员struct

foo都以foo_[standard for me] 为前缀。DLHDEFDLKDEF在需要时使用[模拟]继承/范本的巨集“

trickery” 。

许多函数var都重用我的样式:idx对于索引var

[我从不使用i/j,而是xidx/yidx],cp对于char指针,cnt用于计数,len对于字节长度,等等。因此,我不必“思考”一些小东西[策略]并且可以专注于策略。

以上idx等。等

对我来说是一种“签名风格”。它不一定比其他人更好(或更糟)。这是因为当链接器/加载器只能处理8个字符符号时,我开始使用C,因此简洁是关键。但是,我习惯于使用较短的名称。考虑哪个更清楚/更好:

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

要么:

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

我用的是do { ... } while (0)避免if/else梯子一个 不少

。这称为“一次通过”循环。这被认为是“有争议的”,但是以我的经验,它可以使代码更整洁。就个人而言,我从来没有发现[更多标准]使用的do/while,不能做更容易/有一个更好的环whilefor环-因人而异。实际上,许多语言甚至根本没有do/while

另外,我使用小写字母,除非它始终是大写的#define[或enum]。也就是说,我使用“蛇皮套”(例如fooidx),而 不是

“驼峰皮套”(例如indexForFooArray)。

.proto包含函数原型的文件是自动生成的。这可以节省 大量 时间。 旁注: 确保外部链接中至少有v2,因为Makefile中存在错误。A

make clean会删除.proto。v2不会那样做

这些年来,我发展了自己的风格。原来,Linux内核样式是“从我的借来的”。实际上不是:-)我的是第一位的。但是…他们同时提出了与我的匹配度为99%的内容:/usr/src/kernels/whatever_version/Documentation/CodingStyle

与给定样式(自己的样式)的一致性是关键。对于给定的函数,您不必担心要为变量命名,要使用的缩进或空白行。

这有助于读者/新开发者。他们可以阅读一些功能,查看演奏中的风格,然后由于所有功能具有相似的风格而运行得更快。

所有这些使您可以“更快”,并且在第一次尝试时仍可以获得高质量的代码。我也 很有 经验。

另外,我的代码注释集中在“意图”上。就是说,您希望代码按照实际情况执行什么操作。他们应该回答“什么/为什么”,而代码则是“如何”。

以上是 使用C在Linux Shell中实现输入/输出重定向 的全部内容, 来源链接: utcz.com/qa/425924.html

回到顶部