在关于执行的 C 问题中重新创建管道

问题描述 投票:0回答:1

我实际上正在做项目,我们正在尝试用 C 语言重新创建一个 bash 终端。我在处理代码中的管道时遇到了一些问题。如您所知,当您编写“cat | ls”时,它首先显示 ls 结果,然后打开标准输入,但仅显示一行。 问题是我处理管道的方式,首先它显示 ls 结果作为例外,但在标准输入读取之后直到我进行 CTRL-D。我想让它在第一行之后结束,就像 bash 一样。我想我的管道有问题,但我试图用其他方式重新制作东西,但它总是同样的问题。

我的代码:

void    make_command(t_list *cmds, char **env)
{
    char    *path;
    int     exec;
    
    path = get_path((char *)cmds->content[0], env);
    if (!path)
        return ;
    exec = execve(path, (char **)cmds->content, env);
    if (exec < 0)
        exit_error();
}

void    make_pipe(t_list *cmds, char **env, t_listpids **pids, int *fd_old)
{
    pid_t       pid;
    int         tube[2];

    while (cmds)
    {
        if (pipe(tube) == -1)
            exit_error();
        pid = fork();
        if (pid == 0)
        {
            if (cmds->previous) //only if not first command
            {
                dup2((*fd_old), STDIN_FILENO);
                close(*fd_old);
            }
            if (cmds->next) //only if not last command
            {
                dup2(tube[1], STDOUT_FILENO);
                close(tube[1]);
            }
            make_command(cmds, env); //execute command function with execve
            close(tube[0]);
            close(tube[1]);
            exit(EXIT_SUCCESS);
        }
        else
        {
            add_pids(pid, pids); //add pid to chained list of pids (used to waitpid them after)
            *fd_old = tube[0];
            close(tube[1]);
            cmds = cmds->next;
            while (cmds && ft_strncmp((char *)cmds->content[0], "|", 1) == 0)
                cmds = cmds->next;
        }
    }
}
c bash pipe pid execve
1个回答
1
投票

正如您所知,当您编写“cat | ls”时,它首先显示 ls 结果,然后打开标准输入,但只有一行。

这是对大部分荒谬的管道所发生情况的令人困惑的描述。这是一个更好的方法:

ls
运行完成而不等待任何输入,同时
cat
等待其标准输入上的输入,并在读取一行后终止,而不将该行回显到终端。

两者的重要区别:

  • ls
    命令在强制
    cat
    等待它的意义上不会首先运行。它本身不需要等待任何用户输入,因此它的输出会立即显示。

  • 没有新的标准输入被“打开”。

    cat
    命令从 shell 继承其标准输入,并从该文件读取直到它终止。由于在前台,它接收输入而不是 shell。

此外,

cat
在阅读一行后终止是偶然的。如果有更多的人排队等待它立即阅读,或者如果
ls
由于某种原因运行缓慢,它可能会阅读更多内容。当它确实终止时,那是因为它试图写入一个没有读者的管道。这会导致
SIGPIPE
被传递给它,从而导致它终止。

这都是相关的,因为它指出了您描述的(错误)行为的问题。在你的情况下,

cat
一直读取输入,直到你键入一个
Ctrl-D
马上告诉我它的标准输出根本没有连接到管道,或者它所连接的管道仍然打开在某处阅读。

并且知道要寻找什么,我们可以让自己满意,是的,父进程无法关闭它创建的每个管道的读取端。它在

*fd_old
中一次保存它们,以便能够重定向下一个孩子的标准输入,但在提供该重定向后,它会泄漏其打开的文件描述符的副本。

它只需要在下一个

fork()
之前保持那个FD打开,所以也许你想要的是在
else
块中关闭它,像这样:

        else
        {
            if (cmds->previous) //only if not first command
            {
                close(*fd_old);
            }
            add_pids(pid, pids); //add pid to chained list of pids (used to waitpid them after)
            *fd_old = tube[0];
            close(tube[1]);
            cmds = cmds->next;
            while (cmds && ft_strncmp((char *)cmds->content[0], "|", 1) == 0)
                cmds = cmds->next;
        }

您可能还需要在循环终止后关闭最后一个。我通常会这么认为,但事实上你是通过指针间接存储它,这让我不确定你到底想完成什么。

© www.soinside.com 2019 - 2024. All rights reserved.