在 C 中实现管道 shell

implementing pipes in C shell

我是编程初学者,我正在用 C shell 实现管道。

我发现某些使用 STDIN 的命令,shell 输出开始无法正常工作。例如光标移动了。

这种直接使用STDIN的命令就是这种情况 cat | echo hellotr | ls

我检查了它是否与未关闭的 fd 无关......我在 dup2 后立即关闭了管道的写入端。并且我在管道写入端的管道期间关闭子进程中管道的读取端。

这是代码,抱歉有点乱,它使用了一个 int term->redir->pipe_fd 数组来处理多管道情况:

// For each pipe, it creates the pipe in a new instance of the int array term->redir->pipe_fd.  

int     make_pipe(t_term *term)  
{  
    int pipe_stack;  

    term->redir->pipe_stack++;  
    if (!((pipe_stack = term->redir->pipe_stack) < MAX_FD)  
            || pipe(term->redir->pipe_fd[pipe_stack]) == -1)  
    { 
        err_dup();  
        return (close_pipe(term));  
    }  
    return (0);  
}  

// left_pipe dup2 the write end of (pipe[1]) to the STDOUT, and closes the pipe's write end.  

int     left_pipe(t_term *term)  
{  
    int new_fd;  
    int pipe_stack;  

    new_fd = -1;  
    pipe_stack = term->redir->pipe_stack;  
    term->redir->pipe_write = 1;  
    if ((new_fd = dup2(term->redir->pipe_fd[pipe_stack][WRITE_END],
                    STDOUT_FILENO)) == -1)  
        ft_printf("[fd=2]21sh: %d: LEFT Bad file descriptor\n[/fd]",
                term->redir->pipe_fd[pipe_stack][WRITE_END]);  
    close(term->redir->pipe_fd[pipe_stack][WRITE_END]);  
    term->redir->pipe_fd[pipe_stack][WRITE_END] = -1;  
    return ((new_fd == -1) ? 1 : 0);  
}

// then, sh_exec is called, see below. The read end of the pipe is closes in the child processus. 
// right pipe dup2 the STDIN whith the readend of the pipe (pipe[0]) and closes it.

int     right_pipe(t_term *term)  
{  
    int new_fd;  
    int pipe_stack;  

    new_fd = -1;  
    pipe_stack = term->redir->pipe_stack;  
    term->redir->pipe_write = 0;  
    if (pipe_stack > FD_INIT)  
    {  
        term->redir->pipe_read = 1;  
        if ((new_fd = dup2(term->redir->pipe_fd[pipe_stack][READ_END],
                        STDIN_FILENO)) == -1)  
            ft_printf("[fd=2]21sh: %d: RIGHT Bad file descriptor\n[/fd]",
                    term->redir->pipe_fd[pipe_stack][READ_END]);  
        close(term->redir->pipe_fd[pipe_stack][READ_END]);  
        term->redir->pipe_fd[pipe_stack][READ_END] = -1;  
        term->redir->pipe_stack--;  
    }  
    return ((new_fd == -1) ? 1 : 0);  
}

调用左管道后,调用下面的exec函数,关闭未使用的read_end管道(pipe[0])。 在调用了正确的管道之后,它再次调用 exec 函数来执行命令的正确部分:

void        wait_pipe(char *cmd, int *status)  
{  
    if (waitpid(-1, status, WNOHANG) == -1)  
        print_exec_error(cmd);  
}  

int         sh_exec(char *path, char **cmd, t_term *term)  
{  
    pid_t   father;  
    int     status;  

    status = 0;  
    father = fork();  
    if (father > 0 && term->redir->pipe_stack >= 0)  
        wait_pipe(*cmd, &status);  
    else if (father > 0)  
    {  
        if (waitpid(father, &status, 0) == -1)  
            print_exec_error(*cmd);  
    }  
    if (!father)  
    {  
        if (term->redir->pipe_write)  
            close(term->redir->pipe_fd[term->redir->pipe_stack][READ_END]);  
        if (execve(path, cmd, term->env) == -1)  
        {  
            ft_printf("[fd=2]Filetype unknown\n[/fd]");  
            exit(0);  
        }  
        err_not_found(*cmd);  
    }  
    return (status);  
}  

此外,我不明白 cat 使用以下命令的 bash 行为 cat | echo。 它返回一行的提示然后停止,不太可能 cat 命令单独继续并等待直到它收到停止信号。

如果有人有线索?

Besides, I don't understand the bash behavior of cat with the following command cat | echo. It gives back the prompt for one line and then stops, unlikely the cat command alone which continues and waits until it gets a stop signal.

我将尝试解释这里发生的事情:

$ cat | echo

foobar
$

第一个空行是 echo 命令打印 \nstdout (因为它没有任何参数),然后我得到提示,这是 cat 等待输入,我键入 foobar 并按回车键 (\n)。我怀疑 cat 逐行打印它的输出,因此它尝试将 foobar\n 写入其 stdout,但其 stdout 是 "piped"(|) 到 echostdinecho 已经退出 closing 它的 stdin (因为他无事可做),所以 cat 试图写入一个关闭的管道并得到 SIGPIPESIGPIPE 的默认行为是结束进程。

运行 strace 证实了这一切:

$ strace cat | echo
...
read(0, foobar
"foobar\n", 131072)             = 7
write(1, "foobar\n", 7)                 = -1 EPIPE (Broken pipe)
--- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=6069, si_uid=1000} ---
+++ killed by SIGPIPE +++
$