Linux下C语言自制Shell的pipe管道操作符运行异常求助
问题根因
你代码里的管道实现逻辑存在多处核心错误,直接导致more命令执行异常:
- 执行顺序错误:你在调用
execvp()执行前序命令后才尝试fork子进程运行后序命令,execvp执行成功后不会返回,后续处理管道后段的代码完全不会执行。 - 参数传递错误:两段逻辑都用
command[i].argv调用execvp,相当于把前序ls -l的参数直接传给了more,导致more收到非法参数抛出bad usage报错。 - 管道创建时机错误:管道在第一个子进程内部创建,父进程和后序子进程无法拿到管道的文件描述符,无法实现跨进程数据传递。
- 命令遍历逻辑错误:带管道的
ls -l | more会被解析为两个Command结构体,你当前的循环逻辑只会处理第一个带|分隔符的命令,直接跳过第二个more命令的执行。
修复方案
你需要调整execCommands函数中管道的处理逻辑,核心修改点如下:
- 遍历命令时如果检测到当前命令分隔符为
|,先创建管道,再分别fork两个子进程执行前序、后序命令,执行完成后跳过已经处理的后序命令。 - 前序子进程将标准输出重定向到管道写端,后序子进程将标准输入重定向到管道读端,所有子进程执行完逻辑后及时关闭不使用的管道端。
- 父进程等待两个子进程执行完成后再继续后续逻辑。
核心修改代码示例
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
相关产品推荐
相关产品推荐

