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

如何用dup2()与pipe()在自定义Shell中实现ls | wc命令

如何用dup2()和pipe()实现ls | wc的重定向?

我帮你梳理下原代码里的几个关键问题,然后给出能正常运行的实现方案:

原代码的核心错误

  1. 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)。

  2. execvp 参数数组错误
    你直接把原argv传给execvp,比如第一个子进程执行ls时,原argv里还包含|和wc,这会让ls把这些当成命令参数处理,完全不符合预期。你需要为每个命令单独准备只包含自身和NULL的参数数组。

  3. 管道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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 06:28:55