You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux下C语言自制Shell的pipe管道操作符运行异常求助

问题根因

你代码里的管道实现逻辑存在多处核心错误,直接导致more命令执行异常:

  • 执行顺序错误:你在调用execvp()执行前序命令后才尝试fork子进程运行后序命令,execvp执行成功后不会返回,后续处理管道后段的代码完全不会执行。
  • 参数传递错误:两段逻辑都用command[i].argv调用execvp,相当于把前序ls -l的参数直接传给了more,导致more收到非法参数抛出bad usage报错。
  • 管道创建时机错误:管道在第一个子进程内部创建,父进程和后序子进程无法拿到管道的文件描述符,无法实现跨进程数据传递。
  • 命令遍历逻辑错误:带管道的ls -l | more会被解析为两个Command结构体,你当前的循环逻辑只会处理第一个带|分隔符的命令,直接跳过第二个more命令的执行。

修复方案

你需要调整execCommands函数中管道的处理逻辑,核心修改点如下:

  1. 遍历命令时如果检测到当前命令分隔符为|,先创建管道,再分别fork两个子进程执行前序、后序命令,执行完成后跳过已经处理的后序命令。
  2. 前序子进程将标准输出重定向到管道写端,后序子进程将标准输入重定向到管道读端,所有子进程执行完逻辑后及时关闭不使用的管道端。
  3. 父进程等待两个子进程执行完成后再继续后续逻辑。

核心修改代码示例

void execCommands(Command command[]){
    int fd = 0;
    char dir[BUFSIZE];
    pid_t pid1, pid2;
    int pipi[2];

    for(int i = 0;i < cmdSize;i++){
        if(strcmp(command[i].argv[0], "prompt") == 0){
            strcpy(promptSymbol, command[i].argv[1]);
        }else if(strcmp(command[i].argv[0], "pwd") == 0){
            if(getcwd(cwd, sizeof(cwd)) != NULL){
                    printf("%s\n", cwd );
            }else{
                perror("getcwd() error\n");
            }
        }else if(strcmp(command[i].argv[0], "cd") == 0){
            if(command[i].argv[1] == NULL){
                strcpy(dir, "/home");
            }else if(strcmp(command[i].argv[1], "~") == 0 || strcmp(command[i].argv[1], "~/") == 0){
                strcpy(dir, "/home");
            }else{
                strcpy(dir, command[i].argv[1]);
            }
            if(chdir(dir) < 0){
                printf("No such directory/file %s found\n", dir);
            }
        }else{
            // 检测到管道
            if(strcmp(command[i].sep,"|") == 0 && i+1 < cmdSize){
                if(pipe(pipi) == -1){
                    perror("pipe failed\n");
                    continue;
                }
                // fork第一个子进程执行前序命令
                pid1 = fork();
                if(pid1 == 0){
                    // 重定向标准输出到管道写端
                    close(pipi[0]);
                    dup2(pipi[1], STDOUT_FILENO);
                    close(pipi[1]);
                    // 处理输入重定向
                    if(command[i].stdin_file){
                        fd = open(command[i].stdin_file, O_RDONLY);
                        dup2(fd, STDIN_FILENO);
                        close(fd);
                    }
                    execvp(command[i].argv[0], command[i].argv);
                    perror("execvp first cmd failed");
                    exit(1);
                }
                // fork第二个子进程执行后序命令
                pid2 = fork();
                if(pid2 == 0){
                    // 重定向标准输入到管道读端
                    close(pipi[1]);
                    dup2(pipi[0], STDIN_FILENO);
                    close(pipi[0]);
                    // 处理后序命令的输出重定向
                    if(command[i+1].stdout_file){
                        fd = open(command[i+1].stdout_file, O_WRONLY | O_CREAT, 0644);
                        dup2(fd, STDOUT_FILENO);
                        close(fd);
                    }
                    execvp(command[i+1].argv[0], command[i+1].argv);
                    perror("execvp second cmd failed");
                    exit(1);
                }
                // 父进程关闭管道两端,等待两个子进程执行完成
                close(pipi[0]);
                close(pipi[1]);
                waitpid(pid1, NULL, 0);
                waitpid(pid2, NULL, 0);
                // 跳过已经处理的后序命令
                i++;
            }else{
                // 普通命令执行逻辑
                pid_t pid = fork();
                if(pid == 0){
                    if(command[i].stdin_file){
                        fd = open(command[i].stdin_file, O_RDONLY);
                        dup2(fd, STDIN_FILENO);
                        close(fd);
                    }
                    if(command[i].stdout_file){
                        fd = open(command[i].stdout_file, O_WRONLY | O_CREAT, 0644);
                        dup2(fd, STDOUT_FILENO);
                        close(fd);
                    }
                    execvp(command[i].argv[0], command[i].argv);
                    perror("execvp failed");
                    exit(1);
                }else if(pid > 0){
                    if(strcmp(command[i].sep, "&") != 0){
                        waitpid(pid, NULL, 0);
                    }
                }else{
                    perror("fork failed");
                }
            }
        }
    }
}

额外注意点

你原有代码中cd命令的判断逻辑存在错误:strcmp(command[i].argv[1], "~/")的返回值为0才表示相等,你当前的判断条件只要字符串不相等就会触发跳转,不符合预期,上述修复代码中已经修正了该问题。

内容的提问来源于stack exchange,提问作者agengwan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 21:15:03