在 C 中实现管道 shell
implementing pipes in C shell
我是编程初学者,我正在用 C shell 实现管道。
我发现某些使用 STDIN 的命令,shell 输出开始无法正常工作。例如光标移动了。
这种直接使用STDIN的命令就是这种情况
cat | echo hello
或 tr | 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
命令打印 \n
到 stdout
(因为它没有任何参数),然后我得到提示,这是 cat
等待输入,我键入 foobar
并按回车键 (\n
)。我怀疑 cat
逐行打印它的输出,因此它尝试将 foobar\n
写入其 stdout
,但其 stdout
是 "piped"(|
) 到 echo
的 stdin
和 echo
已经退出 closing 它的 stdin
(因为他无事可做),所以 cat
试图写入一个关闭的管道并得到 SIGPIPE
。 SIGPIPE
的默认行为是结束进程。
运行 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 +++
$
我是编程初学者,我正在用 C shell 实现管道。
我发现某些使用 STDIN 的命令,shell 输出开始无法正常工作。例如光标移动了。
这种直接使用STDIN的命令就是这种情况
cat | echo hello
或 tr | 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
命令打印 \n
到 stdout
(因为它没有任何参数),然后我得到提示,这是 cat
等待输入,我键入 foobar
并按回车键 (\n
)。我怀疑 cat
逐行打印它的输出,因此它尝试将 foobar\n
写入其 stdout
,但其 stdout
是 "piped"(|
) 到 echo
的 stdin
和 echo
已经退出 closing 它的 stdin
(因为他无事可做),所以 cat
试图写入一个关闭的管道并得到 SIGPIPE
。 SIGPIPE
的默认行为是结束进程。
运行 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 +++
$