几乎完美的C Shell管道技巧

14
我正在用C语言编写一个小型的Linux Shell,离完成非常接近。我从用户那里接收命令并将其存储在args中,以空格为分隔符。在下面的示例中,假设args包含以下内容: args[] = {"ls", "-l", "|", "wc"}; 我的函数接受args以及pipes的数量。我已经尽可能地注释了我的代码。以下是代码:
int do_command(char **args, int pipes) {
    // The number of commands to run
    const int commands = pipes + 1;
    int i = 0;

    int pipefds[2*pipes];

    for(i = 0; i < pipes; i++){
        if(pipe(pipefds + i*2) < 0) {
            perror("Couldn't Pipe");
            exit(EXIT_FAILURE);
        }
    }

    int pid;
    int status;

    int j = 0;
    int k = 0;
    int s = 1;
    int place;
    int commandStarts[10];
    commandStarts[0] = 0;

    // This loop sets all of the pipes to NULL
    // And creates an array of where the next
    // Command starts

    while (args[k] != NULL){
        if(!strcmp(args[k], "|")){
            args[k] = NULL;
            // printf("args[%d] is now NULL", k);
            commandStarts[s] = k+1;
            s++;
        }
        k++;
    }



    for (i = 0; i < commands; ++i) {
        // place is where in args the program should
        // start running when it gets to the execution
        // command
        place = commandStarts[i];

        pid = fork();
        if(pid == 0) {
            //if not last command
            if(i < pipes){
                if(dup2(pipefds[j + 1], 1) < 0){
                    perror("dup2");
                    exit(EXIT_FAILURE);
                }
            }

            //if not first command&& j!= 2*pipes
            if(j != 0 ){
                if(dup2(pipefds[j-2], 0) < 0){
                    perror("dup2");
                    exit(EXIT_FAILURE);
                }
            }

            int q;
            for(q = 0; q < 2*pipes; q++){
                    close(pipefds[q]);
            }

            // The commands are executed here, 
            // but it must be doing it a bit wrong          
            if( execvp(args[place], args) < 0 ){
                    perror(*args);
                    exit(EXIT_FAILURE);
            }
        }
        else if(pid < 0){
            perror("error");
            exit(EXIT_FAILURE);
        }

        j+=2;
    }

    for(i = 0; i < 2 * pipes; i++){
        close(pipefds[i]);
    }

    for(i = 0; i < pipes + 1; i++){
        wait(&status);
    }
}

我的问题是,虽然程序在某种程度上执行正确,但它的行为很奇怪,我希望您能帮助我解决问题。
例如,当我运行 "ls | wc" 时,输出结果是 "ls | wc" 的输出结果,但接着它也打印了一个简单的 "ls" 的输出结果,即使它只应该是输出结果的 "wc" 部分。
另一个例子是,当我尝试 "ls -l | wc" 时,"wc" 的第一个数字出现了,但接着 "ls -l" 的输出结果也出现在它下面,即使它只应该是输出结果的 "wc" 部分。
提前感谢您的帮助! :)

能在这里工作。你确定你用正确的参数调用了你的函数吗? - jpalecek
这里也可以正常工作。顺便说一下,do_command 没有按照应该返回的方式返回。 - netcoder
它能工作?哈哈。它能百分之百地正常工作吗?比如,如果你在实际的Linux系统中在程序外键入“ls -l | wc”,它的输出结果是否与我的代码中的一样? - Rick_Sch
我只尝试了 ls | wc,但确实输出相同。 - jpalecek
有一个错误影响到了 ls -l | wc(有效地变成了 ls -l | wc -l)。除此之外,它对我来说是有效的。你应该尝试使用 strace -f yourprogram 查看它实际执行了什么操作。 - jpalecek
1个回答

9

好的,我找到了一个小错误。这个

       if( execvp(args[place], args) < 0 ){

应该是

       if( execvp(args[place], args+place) < 0 ){

您的版本在所有其他命令中都使用了第一个命令的参数。除此之外,对我来说它很好用。


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