我正在编写一个小程序来模仿Unix管道系统(例如“cat file1.txt | grep keyword | wc
”)。
我可以设法使用dup2()
和管道从stdout收集程序的输出,但我无法知道如何将其输入到下一个进程。
起初我认为它很简单,就像:
write(stdin, buffer, buffer_size);
但这不适合我。关于stdout的信息很多,但关于stdin的信息差不多。
任何帮助都会很棒。
答案 0 :(得分:2)
据我了解你的问题,你的意思是你想从你的程序中执行命令cat file1.txt | grep keyword | wc
。
您可以通过在pipe()
的程序之间设置一些管道来实现此目的。
然后fork
几次为您要执行的每个程序。
在不同的分叉进程中,将stdin
和stdout
设置为创建的管道的正确结尾。
设置完所有内容后,您可以拨打exec()
来使用正确的参数执行cat
,grep
和wc
等不同的程序。
您的程序可以在wc
程序之后从管道中读取以获得最终输出。
答案 1 :(得分:1)
您的代码不会进行任何阅读或写作;它只是管道(整理管道)。
您将创建两个管道和两个子项。第一个孩子将运行cat
命令;第二个是grep
命令,父节点将运行wc
程序。由于程序没有路径名,因此代码将使用execvp()
。
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
static void err_exit(const char *fmt, ...);
static void exec_head(char **args, int *p1, int *p2);
static void exec_tail(char **args, int *p1, int *p2);
static void exec_middle(char **args, int *p1, int *p2);
static void exec_cmd(char **args, int *p1, int *p2);
int main(void)
{
char *cmd1[] = { "cat", "file1.txt", 0 };
char *cmd2[] = { "grep", "keyword", 0 };
char *cmd3[] = { "wc", 0 };
int pipe12[2];
int pipe23[2];
pid_t pid1;
pid_t pid2;
if (pipe(pipe12) != 0 || pipe(pipe23) != 0)
err_exit("Failed to create pipes");
if ((pid1 = fork()) < 0)
err_exit("Failed to fork (child1)");
else if (pid1 == 0)
exec_head(cmd1, pipe12, pipe23);
else if ((pid2 = fork()) < 0)
err_exit("Failed to fork (child2):");
else if (pid2 == 0)
exec_middle(cmd2, pipe12, pipe23);
else
exec_tail(cmd3, pipe12, pipe23);
/*NOTREACHED*/
return(-1);
}
/* Execute head process in pipeline */
/* Close both pipes in p2; connect write end of pipe p1 to stdout */
static void exec_head(char **args, int *p1, int *p2)
{
if (dup2(p1[1], 1) < 0)
err_exit("Failed to duplicate file descriptor to stdout (%s)", args[0]);
exec_cmd(args, p1, p2);
/*NOTREACHED*/
}
/* Execute tail process in pipeline */
/* Close both pipes in p1; connect read end of p2 to standard input */
static void exec_tail(char **args, int *p1, int *p2)
{
if (dup2(p2[0], 0) < 0)
err_exit("Failed to duplicate file descriptor to stdin (%s)", args[0]);
exec_cmd(args, p1, p2);
/*NOTREACHED*/
}
/* Execute middle command in pipeline */
/* Connect read end of p1 to stdin; connect write end of p2 to stdout */
static void exec_middle(char **args, int *p1, int *p2)
{
if (dup2(p1[0], 0) < 0)
err_exit("Failed to duplicate file descriptor to stdin (%s)", args[0]);
if (dup2(p2[1], 1) < 0)
err_exit("Failed to duplicate file descriptor to stdout (%s)", args[0]);
exec_cmd(args, p1, p2);
/*NOTREACHED*/
}
/* Close all descriptors for pipes p1, p2 and exec command */
static void exec_cmd(char **args, int *p1, int *p2)
{
close(p1[0]);
close(p1[1]);
close(p2[0]);
close(p2[1]);
execvp(args[0], args);
err_exit("Failed to execute %s", args[0]);
/*NOTREACHED*/
}
static void err_exit(const char *fmt, ...)
{
int errnum = errno;
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, ":%d: %s", errnum, strerror(errnum));
putc('\n', stderr);
exit(EXIT_FAILURE);
}
通过更多的重构工作和仔细处理管道,您可以消除exec_head()
,exec_tail()
和exec_middle()
函数(指定描述符成为标准输入,或者-1,如果它应该单独保留stdin;指定描述符成为stdout,如果它应该单独保留stdout,则为-1),并且可以通过使用单个数组4并修改对pipe()
系统调用的调用来简化管道处理因此。然后可以毫不费力地推广......代码应该使用STDIN_FILENO
和STDOUT_FILENO
而不是0和1作为dup2()
的第二个参数。管道的读取和写入端没有定义的常量(enum { PIPE_READ, PIPE_WRITE };
在这里可能很有用。)