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); }
问题定位与解决办法
核心问题
命令解析逻辑漏洞:
execute_pipeline_redirection中,管道的判断条件仅匹配curr->next->Token_str[0] == '|',对于ls | wc > a里的wc,其后续是重定向而非管道符,导致wc进程未被创建。此时ls的输出写入管道后,因读端无人读取,管道写缓冲区满导致ls阻塞;父进程等待未创建的PID,陷入无限等待。而ls | cat fichier.txt > a中cat后续是文件参数,能被管道条件匹配,进程正常创建。管道与重定向的耦合缺失:当前实现将管道和重定向作为独立分支处理,未考虑管道最后一个命令的输出需要被重定向的场景,导致重定向逻辑无法作用于管道末尾的命令。
修复步骤
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
相关产品推荐
相关产品推荐

