gpt4 book ai didi

c - 使用 C 在 Linux shell 中实现输入/输出重定向

转载 作者:太空狗 更新时间:2023-10-29 12:12:58 24 4
gpt4 key购买 nike

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

当我在命令行中运行它时,我得到提示并键入类似“ls”的内容,这在我添加 for 循环之前有效,但现在我只得到“% am i here”,如果我按回车键它只是一直给我同样的台词。我的目标是能够键入“ls > output”并让它工作。我认为输入重定向有效,但老实说我什至没有玩过它,因为我对输出重定向发生了什么感到非常困惑。任何帮助将不胜感激,我已经在 15 条线路上花费了 4 个小时试图让它工作。

#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 重定向,并将它们从传递给 child 的参数列表中删除。

这是更正后的代码[请原谅不必要的样式清理]:

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

更新:

If you're still around do you think you could help me with creating a pipe?

当然。它太大了,无法在这里发布。请参阅:http://pastebin.com/Ny1w6pUh


Wow did you create all 3300 lines?

是的。

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

但是...它由我以前做过的片段/概念组成:tgb、FWD、BTV、sysmagic。请注意 struct foo 的所有结构成员都带有前缀 foo_ [我的标准]。使用 DLHDEFDLKDEF 来模拟继承/模板的宏“技巧”也是我[必要时]做的事情。

许多函数变量重用了我的样式:idx 用于索引变量 [我永远不会使用 i/j,而是使用 xidx/yidx] , cp 表示字符指针,cnt 表示计数,len 表示字节长度,等等。因此,我不必“思考”小东西 [战术],可以专注于战略。

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

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

或者:

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

我使用 do { ... } while (0) 来避免 if/else 阶梯很多。这称为“一次性”循环。这被认为是“有争议的”,但根据我的经验,它使代码更清晰。就个人而言,我从来没有发现使用 do/while 循环的 [更标准] 用法不能用 while 更容易/更好地完成for 循环——YMMV。事实上,许多语言甚至根本没有 do/while

此外,我使用小写字母,除非它是一个总是大写的 #define [或 enum]。也就是说,我使用“蛇形案例”(例如 fooidx)而不是“驼峰案例”(例如indexForFooArray)。

包含函数原型(prototype)的.proto 文件是自动生成的。这是一个巨大的节省时间的方法。 旁注:请确保您至少拥有来自外部链接的 v2,因为 Makefile 中存在错误。 make clean 会删除 .proto。 v2 不会那样做

多年来,我形成了自己的风格。事实证明,linux 内核风格是“从我那里借来的”。实际上不是 :-) 我的排在第一位。但是......他们同时提出了与我的 99% 匹配的东西:/usr/src/kernels/whatever_version/Documentation/CodingStyle

与特定风格 [自己的] 保持一致是关键。对于给定的函数,您不必担心变量的名称、使用的缩进或空行。

这有助于读者/新开发者。他们可以阅读一些函数,了解游戏中的风格,然后走得更快,因为所有函数都具有相似的风格。

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

此外,我的代码注释侧重于“意图”。也就是说,您希望代码在现实世界中做什么。他们应该回答“是什么/为什么”,代码是“如何”。

关于c - 使用 C 在 Linux shell 中实现输入/输出重定向,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35569673/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com