如何用dup2()与pipe()在自定义Shell中实现ls | wc命令
如何用dup2()和pipe()实现
ls | wc的重定向? 我帮你梳理下原代码里的几个关键问题,然后给出能正常运行的实现方案:
原代码的核心错误
dup2 参数顺序搞反
dup2()的正确用法是dup2(目标文件描述符, 要替换的文件描述符)——意思是把「要替换的fd」重定向到「目标fd」。你写的dup2(stdout, arr[1])完全搞反了,应该是把标准输出(STDOUT_FILENO)替换成管道的写端,也就是dup2(arr[1], STDOUT_FILENO)。同理,输入重定向的dup2(0, arr[0])要改成dup2(arr[0], STDIN_FILENO)。execvp 参数数组错误
你直接把原argv传给execvp,比如第一个子进程执行ls时,原argv里还包含|和wc,这会让ls把这些当成命令参数处理,完全不符合预期。你需要为每个命令单独准备只包含自身和NULL的参数数组。管道fd未及时关闭
父进程在fork子进程后,必须关闭自己不需要的管道端,否则管道的写端不会被完全关闭,wc会一直等待输入,永远无法退出。
修正后的完整代码
假设你的输入是类似./program ls | wc的格式,argv已经被正确分割,下面是能实现ls | wc功能的代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/wait.h> int main(int argc, char *argv[]) { // 检查输入是否包含管道符 if (argc >= 4 && !strcmp(argv[2], "|")) { int pipefd[2]; // 创建管道:pipefd[0]是读端,pipefd[1]是写端 if (pipe(pipefd) == -1) { perror("pipe failed"); exit(EXIT_FAILURE); } // 第一个子进程:执行ls,输出到管道 pid_t ls_pid = fork(); if (ls_pid == -1) { perror("fork ls failed"); exit(EXIT_FAILURE); } if (ls_pid == 0) { // 子进程不需要读端,先关闭 close(pipefd[0]); // 把标准输出重定向到管道写端 if (dup2(pipefd[1], STDOUT_FILENO) == -1) { perror("dup2 stdout failed"); exit(EXIT_FAILURE); } // 重定向完成后,原写端可以关闭 close(pipefd[1]); // 准备ls的参数数组 char *ls_cmd[] = {argv[1], NULL}; execvp(ls_cmd[0], ls_cmd); // 如果execvp返回,说明执行失败 perror("execvp ls failed"); exit(EXIT_FAILURE); } // 第二个子进程:执行wc,从管道读输入 pid_t wc_pid = fork(); if (wc_pid == -1) { perror("fork wc failed"); exit(EXIT_FAILURE); } if (wc_pid == 0) { // 子进程不需要写端,先关闭 close(pipefd[1]); // 把标准输入重定向到管道读端 if (dup2(pipefd[0], STDIN_FILENO) == -1) { perror("dup2 stdin failed"); exit(EXIT_FAILURE); } // 重定向完成后,原读端可以关闭 close(pipefd[0]); // 准备wc的参数数组 char *wc_cmd[] = {argv[3], NULL}; execvp(wc_cmd[0], wc_cmd); // 如果execvp返回,说明执行失败 perror("execvp wc failed"); exit(EXIT_FAILURE); } // 父进程必须关闭管道的两端,否则wc会一直等待输入 close(pipefd[0]); close(pipefd[1]); // 等待两个子进程执行完成 waitpid(ls_pid, NULL, 0); waitpid(wc_pid, NULL, 0); return EXIT_SUCCESS; } fprintf(stderr, "Invalid input format. Use: ./program ls | wc\n"); return EXIT_FAILURE; }
关键逻辑说明
- 管道创建:
pipe(pipefd)创建一个匿名管道,一端用于写(pipefd[1]),一端用于读(pipefd[0])。 - 第一个子进程(ls):关闭管道读端,把标准输出重定向到管道写端,这样
ls的所有输出都会写入管道。 - 第二个子进程(wc):关闭管道写端,把标准输入重定向到管道读端,这样
wc会从管道读取输入,也就是ls的输出。 - 父进程的管道关闭:必须关闭管道的两端,否则管道的写端不会被标记为EOF,
wc会一直阻塞等待输入。 - 等待子进程:用
waitpid确保两个子进程都执行完成后,父进程才退出。
内容的提问来源于stack exchange,提问作者TomeRaider
相关产品推荐
相关产品推荐

