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

自定义Minishell中cat | cat | ls管道异常问题求助

Minishell管道异常问题:cat | cat | ls中cat未等待标准输入

问题场景

我和伙伴开发了迷你Shell(Minishell),负责解析与执行模块。大部分功能运行正常,但执行cat | cat | ls命令时,ls输出正常,但cat未按预期等待标准输入就直接返回提示符。怀疑是管道未在正确时机关闭,但无法定位具体位置。

核心代码

void    executing_commands(t_minish *data)
{
    t_cmd   *cmd;
    int process_status;

    process_status = 0;
    cmd = data->cmds;
    if (!cmd->full_cmd)
    return ;
    creating_pipes(data);
    while (cmd && cmd->full_cmd)
    cmd = creating_child(&cmd, data);
    closing_all_fd(data);
    cmd = data->cmds;
    while (cmd)
    {
        waitpid(data->child, &process_status, 0);
        if (data->cmds && !check_parent_builtin(&cmd))
            g_status = WEXITSTATUS(process_status);
        cmd = cmd->next;
    }
}


t_cmd   *creating_child(t_cmd **cmd, t_minish *data)
{
    int pid;

    if (check_parent_builtin(cmd))
        executing_builtin(data, cmd);
    else
    {
        pid = fork();
        if (ft_memcmp((*cmd)->full_cmd[0], "minishell", 10))
            set_signals(EXEC);
        data->child = pid;
        if (pid == -1)
        {
            closing_all_fd(data);
            error_manager(2, data, NULL);
        }
        else if (pid == 0)
            child_process(data, cmd);
    }
    return ((*cmd)->next);
}


void    child_process(t_minish *data, t_cmd **cmd)
{
    switching_input_output(data, cmd);
    closing_all_fd(data);
    if (check_child_builtin(cmd))
        executing_builtin(data, cmd);
    else
        launching_command(data, cmd);
}

void    switching_input_output(t_minish *data, t_cmd **cmd)
{
    if ((*cmd)->output > 1)
    {
        if (dup2((*cmd)->output, STDOUT_FILENO) < 0)
            error_manager(6, data, cmd);
        close((*cmd)->output);
    }
    if ((*cmd)->input)
    {
        if (dup2((*cmd)->input, STDIN_FILENO) < 0)
            error_manager(6, data, cmd);
        close((*cmd)->input);
    }
    if ((*cmd)->file_in)
    {
        if (dup2((*cmd)->file_in, STDIN_FILENO) < 0)
            error_manager(6, data, cmd);
        close((*cmd)->file_in);
    }
    if ((*cmd)->file_out)
    {
        if (dup2((*cmd)->file_out, STDOUT_FILENO) < 0)
            error_manager(6, data, cmd);
        close((*cmd)->file_out);
    }
}

void    launching_command(t_minish *data, t_cmd **cmd)
{
    if (execve((*cmd)->full_path, (*cmd)->full_cmd, data->env_table) == -1)
        error_manager(3, data, cmd);
}

void    closing_all_fd(t_minish *data)
{
    t_cmd   *cmd;

    cmd = data->cmds;
    while (cmd)
    {
        if (cmd->input)
            close(cmd->input);
        if (cmd->output > 1)
            close(cmd->output);
        cmd = cmd->next;
    }
}

void    closing_fd_if_redirections(t_minish *data)
{
    t_cmd   *cmd;

    cmd = data->cmds;
    while (cmd)
    {
        if (cmd->file_in && cmd->input)
            close(cmd->input);
        cmd = cmd->next;
    }
}


void    creating_pipes(t_minish *data)
{
    int     fd[2];
    t_cmd   *cmd;

    cmd = data->cmds;
    while (cmd->next != NULL)
    {
        if (pipe(fd) == -1)
            error_manager(1, data, NULL);
        cmd->output = fd[1];
        cmd->next->input = fd[0];
        if (!cmd->next->next)
            cmd->next->last = 1;
        cmd = cmd->next;
    }
    closing_fd_if_redirections(data);
}

问题根源

核心问题是父进程未及时关闭管道的写端,导致子进程的管道读端无法触发EOF:

  1. 执行cat | cat | ls时,第一个cat的输出连到管道1写端,第二个cat的输入连管道1读端、输出连管道2写端,ls的输入连管道2读端。
  2. 父进程在创建完所有子进程后才调用closing_all_fd关闭管道,此时父进程仍持有所有管道的写端引用。
  3. 第二个cat的输出管道(管道2)会在ls执行完毕后关闭读端,导致第二个cat收到SIGPIPE退出;同时父进程仍持有管道1的写端,第一个cat的读端(终端stdin)未被阻塞,反而因为下游管道异常直接退出,不会等待用户输入。

修复方案

1. 父进程创建子进程后立即关闭无用管道

在creating_child函数的父进程分支(pid > 0)中,关闭当前命令的输出管道和下一个命令的输入管道——父进程不需要继续持有这些描述符:

else // pid > 0,父进程逻辑
{
    // 关闭当前cmd的输出管道(子进程已继承并使用)
    if ((*cmd)->output > 1)
        close((*cmd)->output);
    // 关闭下一个cmd的输入管道(子进程已继承并使用)
    if ((*cmd)->next && (*cmd)->next->input)
        close((*cmd)->next->input);
}

2. 移除父进程最后统一关闭管道的逻辑

删除executing_commands函数中的closing_all_fd(data)调用,因为管道已经在创建子进程时逐步关闭,避免父进程长时间持有管道写端。

3. 验证子进程管道关闭逻辑

当前子进程中调用closing_all_fd是正确的,确保子进程只保留自己需要的标准输入输出描述符,其他管道都被关闭。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 12:47:56