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

C语言实现Bash管道与重定向异常:ls | wc >a无法正常工作

问题描述

在C语言实现Bash的管道与输出重定向功能时,遇到如下异常:

  • 单独使用管道或重定向时功能正常
  • 执行ls | wc > a时,能成功创建文件a,但无法将ls | wc的输出写入该文件,进程陷入无限等待PID的状态
  • 执行ls | cat fichier.txt > a时功能正常

相关实现代码

pipeline.c

void create_pipe(int pipe_fd[2], int i, int last) 
{
    if (i < (last - 1))
    {
        if (pipe(pipe_fd) == -1) 
        {
            perror("pipe");
            exit(EXIT_FAILURE);
        }
    }
}

void close_pipes_in_parent(int i, t_data *data, int *input_fd) 
{
    if (i != data->first)
        close(*input_fd);
    if (i < data->last) 
    {
        close(data->pipe_fd[1]);
        *input_fd = data->pipe_fd[0];
    }

}


pid_t fork_process_pipe(int i, int input_fd, t_data *data, t_Token *curr, char **envp, t_Token *lst_tok) 
{
    pid_t pid = fork(); 

    if (pid == 0)
    { 
        printf("111111111111111111");
        if (i != data->first) 
        {
                dup2(input_fd, STDIN_FILENO);
                close(input_fd);
        }
        if (i < data->last) 
        {
                dup2(data->pipe_fd[1], STDOUT_FILENO);
                close(data->pipe_fd[0]);
                close(data->pipe_fd[1]);
        }
        data->path_exe = ft_get_reading(data, curr, envp);
        if(!data->path_exe)
        {
            printf("Bash : command not found : %s\n", data->cmd[0]);
            ft_free_tabtab(data->cmd);
            free_token(lst_tok);
            exit(EXIT_FAILURE);
        }
        if (execve(data->path_exe, data->cmd, envp) == -1) 
        {
            ft_free_tabtab(data->cmd);
            free(data->path_exe);
            exit(EXIT_FAILURE);
        }
        free(curr->Token_str);
        free(curr);
    }
    return (pid);
}

redir.c

int *creat_fd_out(t_data *data, t_Token *curr, int *output_fd)
{
    int i;

    i = 0;
    while(i < data->nb_file)
    {
        output_fd[i] =  open(curr->next->next->Token_str, O_CREAT | O_WRONLY | O_TRUNC, 0644);
        if (output_fd[i] == -1) 
            exit(EXIT_FAILURE);
        i++;
        curr = curr->next->next ;
    }
    return(output_fd);
}

void close_fd_in(t_data *data, int *output_fd)
{
    int i;

    i = 0;
    while(i < data->nb_file)
    {    
        close(output_fd[i]);
        i++;
    }
}

pid_t fork_process_redi(t_data *data, t_Token *curr, char **envp, t_Token *lst_tok) 
{
    pid_t pid = fork(); 
    int *output_fd = malloc(data->nb_file * sizeof(int)); 
    
    if (pid == 0)
    { 
        if ((curr->type == E_WORD && curr->next != NULL) && (curr->next->Token_str[0] == '>'))
        {  
            data->path_exe = ft_get_reading(data, curr, envp);
            if(!data->path_exe)
            {
                printf("Bash : command not found : %s\n", data->cmd[0]);
                ft_free_tabtab(data->cmd);
                free_token(lst_tok);
            }
            output_fd = creat_fd_out(data, curr, output_fd);
            dup2(output_fd[data->nb_file -1], STDOUT_FILENO);
            close_fd_in(data, output_fd);

        }
        if (execve(data->path_exe, data->cmd, envp) == -1) 
        {
            ft_free_tabtab(data->cmd);
            free(data->path_exe);
            exit(EXIT_FAILURE);
        }
        free(curr->Token_str);
        free(curr);
    }
    return (pid);
}

lanch_exe.c

void execute_pipeline_redirection(t_data *data, t_Token *list_token, char **envp) 
{
    int index_fork;
    int input_fd;
    t_Token *curr;

    data->last = data->nb_cmd;
    curr = list_token;
    input_fd = STDIN_FILENO;
    data->first = 0;
    index_fork = 0;
    pid_t pid[data->last];
    

    while (curr != NULL) 
    {   
        if ((curr->type == E_WORD && curr->next != NULL) && (curr->next->Token_str[0] == '>') && (curr->next->next->type == E_FILE))   
        {
            pid[index_fork] = -1;        
            pid[index_fork] = fork_process_redi(data, curr, envp, list_token);
            index_fork++;
        }
        if (((curr->type == E_WORD || curr->next->type ==  E_FILE ) && curr->next != NULL && curr->next->Token_str[0] == '|') && (curr->type == E_WORD && curr->next == NULL))
        {        
            pid[index_fork] = -1;        
            create_pipe(data->pipe_fd, index_fork, data->last);
            pid[index_fork] = fork_process_pipe(index_fork, input_fd, data, curr, envp, list_token);
            close_pipes_in_parent(index_fork, data, &input_fd);
            index_fork++;

        }
        curr = curr->next;
    }
    wait_for_children(data->last, pid);
}

问题定位与解决办法

核心问题

  1. 命令解析逻辑漏洞:execute_pipeline_redirection中,管道的判断条件仅匹配curr->next->Token_str[0] == '|',对于ls | wc > a里的wc,其后续是重定向而非管道符,导致wc进程未被创建。此时ls的输出写入管道后,因读端无人读取,管道写缓冲区满导致ls阻塞;父进程等待未创建的PID,陷入无限等待。而ls | cat fichier.txt > a中cat后续是文件参数,能被管道条件匹配,进程正常创建。

  2. 管道与重定向的耦合缺失:当前实现将管道和重定向作为独立分支处理,未考虑管道最后一个命令的输出需要被重定向的场景,导致重定向逻辑无法作用于管道末尾的命令。

修复步骤

1. 修正命令解析逻辑

修改execute_pipeline_redirection,按管道分割命令单元,每个单元可能包含重定向,确保所有命令(包括带重定向的末尾命令)都被处理:

void execute_pipeline_redirection(t_data *data, t_Token *list_token, char **envp) 
{
    int index_fork;
    int input_fd;
    t_Token *curr;
    t_Token *cmd_start;

    data->last = data->nb_cmd;
    curr = list_token;
    input_fd = STDIN_FILENO;
    data->first = 0;
    index_fork = 0;
    pid_t pid[data->last];
    
    while (curr != NULL) 
    {   
        cmd_start = curr;
        // 定位当前命令的结束位置(下一个管道符或列表末尾)
        while (curr != NULL && !(curr->next != NULL && curr->next->Token_str[0] == '|')) {
            curr = curr->next;
        }

        // 为当前命令创建管道并处理重定向
        create_pipe(data->pipe_fd, index_fork, data->last);
        pid[index_fork] = fork_process_pipe_with_redir(index_fork, input_fd, data, cmd_start, envp, list_token);
        close_pipes_in_parent(index_fork, data, &input_fd);
        index_fork++;

        // 跳过管道符,进入下一个命令单元
        if (curr != NULL) {
            curr = curr->next->next;
        }
    }
    wait_for_children(data->last, pid);
}

2. 合并管道与重定向的进程创建逻辑

新增fork_process_pipe_with_redir函数,在创建管道进程时同时处理重定向:

pid_t fork_process_pipe_with_redir(int i, int input_fd, t_data *data, t_Token *cmd_start, char **envp, t_Token *lst_tok) 
{
    pid_t pid = fork(); 

    if (pid == 0)
    { 
        // 处理管道输入
        if (i != data->first) 
        {
            dup2(input_fd, STDIN_FILENO);
            close(input_fd);
        }

        // 处理当前命令的输出重定向
        t_Token *curr = cmd_start;
        int output_fd = STDOUT_FILENO;
        while (curr != NULL && !(curr->next != NULL && curr->next->Token_str[0] == '|')) {
            if (curr->type == E_WORD && curr->next != NULL && curr->next->Token_str[0] == '>') {
                output_fd = open(curr->next->next->Token_str, O_CREAT | O_WRONLY | O_TRUNC, 0644);
                if (output_fd == -1) {
                    perror("open");
                    exit(EXIT_FAILURE);
                }
                dup2(output_fd, STDOUT_FILENO);
                close(output_fd);
                // 跳过重定向符和文件名
                curr = curr->next->next;
            } else {
                curr = curr->next;
            }
        }

        // 处理管道输出(仅当不是最后一个命令时)
        if (i < data->last - 1) 
        {
            dup2(data->pipe_fd[1], STDOUT_FILENO);
            close(data->pipe_fd[0]);
            close(data->pipe_fd[1]);
        }

        // 执行命令
        data->path_exe = ft_get_reading(data, cmd_start, envp);
        if(!data->path_exe)
        {
            printf("Bash : command not found : %s\n", data->cmd[0]);
            ft_free_tabtab(data->cmd);
            free_token(lst_tok);
            exit(EXIT_FAILURE);
        }
        if (execve(data->path_exe, data->cmd, envp) == -1) 
        {
            ft_free_tabtab(data->cmd);
            free(data->path_exe);
            exit(EXIT_FAILURE);
        }
        exit(EXIT_FAILURE);
    }
    return (pid);
}

3. 清理冗余代码

删除原有的fork_process_redi函数,重定向逻辑已合并到管道进程创建中。

额外注意事项

  • 确保ft_get_reading能正确解析包含重定向的命令,自动忽略重定向符和文件名,只提取命令及参数;
  • 父进程需严格关闭所有管道描述符,避免子进程因管道未关闭而阻塞;
  • 检查wait_for_children函数,确保只等待有效的PID,避免无效等待。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 07:52:32