在用C语言编写的自定义Shell程序中重定向I/O

5
我一直在编写一个自定义shell脚本,但在使用下面的代码重定向输出时出现了小错误。目前这段代码可以完美地工作,但是当传递给execvp args时,会抛出诸如(ls ">"没有此文件或目录)的错误。我知道这是因为它将整个args[]传递给父shell,而这并不起作用。添加args[j] = NULL会去掉"<"/">",从而修复错误,但也导致重定向不再起作用。我该如何既不抛出错误又能正常工作?我已经阅读了多个版本的这个问题,但似乎找不到答案。非常感谢您的帮助。
switch (fork()){
        case -1:
        fprintf(stderr, "error forking");

        case 0://CHILD

        for(int j = 0; j < size; j++){

            if(!strcmp(args[j], "<")){//looking for input character
            ++ext;
            if((in = open(args[j+1], O_RDONLY)) < 0){//open file for reading
                fprintf(stderr, "error opening file\n");
            }
            dup2(in, STDIN_FILENO);//duplicate stdin to input file
            close(in);//close after use
            //args[j] = NULL;
                }//end input chech


            if(!strcmp(args[j],">")){//looking for output character
            ++ext;
                out = creat(args[j+1], 0644);//create new output file           
            dup2(out, STDOUT_FILENO);//redirect stdout to file
            close(out);//close after usere  
        //  args[j] = NULL;
            }//end output check 

            if(!strcmp(args[j], ">>")){//looking for append
            ++ext;
            int append = open(args[j+1],O_CREAT | O_RDWR | O_APPEND, 0644);
                dup2(append, STDOUT_FILENO);
                close(append);
             // args[j] = NULL;
            }                

         }//end loop


        execvp(args[0],args);//execute in parent
        fprintf(stderr, "error in child execi \n");//error
        exit(0);    

         default://PARENT
        wait(&status);  //wait for child to finish  
    }//end switch

你是指“shell程序”(替代标准shell,如Bash),还是指“shell脚本”(由特定shell运行的程序)?要小心;程序不一定是脚本,如果您发布C源代码,则不是发布脚本-至少使用传统含义。此外,请注意,有一个特定的C shell,其语法与Bash和其他源自Bourne shell的shell不同。因此,您可能意味着“在C中编写的自定义shell程序中重定向I/O”或类似内容。 - Jonathan Leffler
1个回答

5
当您解析重定向(例如<>>>)并执行open/dup2时,您需要从传递给execvp的参数列表中将它们去除
因此,针对给定的args,您需要一个第二个(例如args_clean)参数列表,您只需复制程序名称及其参数。
此外,在args中跳过重定向文件需要额外增加j的一个增量(即仅执行j + 1是不等效的)。
以下是清理后的子代码[请原谅过多的样式清理]:
char *args_clean[size];
int cleanidx = 0;

for (int j = 0; j < size; j++) {
    if (!strcmp(args[j], "<")) {        // looking for input character
        ++j;
        if ((in = open(args[j], O_RDONLY)) < 0) {   // open file for reading
            fprintf(stderr, "error opening file\n");
        }
        dup2(in, STDIN_FILENO);         // duplicate stdin to input file
        close(in);                      // close after use
        continue;
    }                                   // end input chech

    if (!strcmp(args[j], ">")) {        // looking for output character
        ++j;
        out = creat(args[j], 0644); // create new output file
        dup2(out, STDOUT_FILENO);       // redirect stdout to file
        close(out);                     // close after usere
        continue;
    }                                   // end output check

    if (!strcmp(args[j], ">>")) {       // looking for append
        ++j;
        int append = open(args[j], O_CREAT | O_RDWR | O_APPEND, 0644);

        dup2(append, STDOUT_FILENO);
        close(append);
        continue;
    }

    args_clean[cleanidx++] = args[j];
}                                       // end loop

args_clean[cleanidx] = NULL;
execvp(args_clean[0], args_clean);                  // execute in parent
fprintf(stderr, "error in child execi \n"); // error
exit(0);

同样的,关于管道,您可以看一下这里提供的答案:fd leak, custom Shell

如果需要完整的Shell实现,可以参考我的答案:Implementing input/output redirection in a Linux shell using C 并查看链接中嵌入的pastebin。


现在这就是自我推销——干得好!(同时也是一个很好的回答……) - David C. Rankin
@DavidC.Rankin 谢谢。是的,有点厚颜无耻。但是,请看这个 Stack Overflow 的答案 [并不是 我的] https://dev59.com/llQK5IYBdhLWcg3wHcSi#52442453 ,以及我的评论给 Vittorio(即我赞同他所说的话,+1 并告诉他)。然后 Peter Schneider 也加入了讨论。我[仅仅]链接到了我在代码审查上做的一个回答,因为我想反驳 Peter 在评论中的观点在链接的回答里已经包含了。嗯,又多了一次自我推销,但这次是为了维护原则 :-) 而且,在代码审查的回答中我必须捍卫我的原则,因为在我澄清之前我被投了-3 的反对票。 - Craig Estey
实际上,没有太多的修改。我浏览了一下你的 open/dup2/close 代码,看起来很不错,所以我没有费心去严格检查它,也没有对其进行任何更改。许多 OP(原始帖)在这方面都有困难,尤其是在管道方面,所以你已经领先了。 - Craig Estey
哇,我从没想过这样做,但它解决了所有问题,非常感谢你的帮助! - snipshow7
结局证明一切 :) - David C. Rankin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接