如何在C中从stdin写入管道

How to write from stdin to a pipe in C

我有一个被参数调用的程序:

1
 pipeline -f outfile2 < infile > outfile1

它应该模仿bash脚本的功能:

1
 (rev | sort | uniq -c | tee outfile2| wc) < infile > outfile1

我了解如何设置管道,但不了解如何进行初步阅读。 我已经将outfile2文件名捕获到一个变量中,但是我认为可以保留外部两个文件,并且操作系统会将它们分别作为stdin和stdout来选择。 如何在父进程中以编程方式将stdin读入管道?

UPDATE之后:以下代码不使用命令行参数修改输出文件:pipeline -f outfile2 outfile1
显然带有真实文件名。

主要:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
char *fp;
int c;

/* parse command line arguments */
parse_argv(argc, argv);

if (pipe(pipeOne) == -1){ /* Create the pipe */
    perror("pipe one");
    error(-1);
}


if (pipeOne[1] != 1){
    dup2(pipeOne[1], stdout);
    exit(-1);
}

while ((c = getc(stdin)) != EOF){
      if ((write(pipeOne[1], c, 1)) < 1)
      {
          perror("Write to pipe failed.");
          exit(-1);
     }
 }

wc_child();
/* begin reading file to pipe */
if (close(pipeOne[0]) == -1){ /* Close unused read end */
    perror("closing pipes");
    exit(-1);
}
 close (pipeOne[1]);

wc_child:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void wc_child(){
int numread;
switch (fork()) { /* Create a child process */
    case -1:
        perror("fork for rev_child");
    case 0: /* Child */
        /*if (close(pipeOne[1]) == -1) /* Close unused write end */
        /*perror("closing pipes");*/
        if (close(pipeOne[1]) == -1){ /* Close unused write end */
            perror("closing pipes");
            exit(-1);
            }
        dup2(pipeOne[0], stdin);
        /* dup2(pipeFive[0], stdin);*/
        /* Not sure how to set up stdout since it will be going to a file.*/

        for(;;){
            execlp("wc","wc");
        }

        break;
    default: /* Parent */
        return 0;
}
return -1; /*return -1 because it never should reach this code.  If it does that indicates a problem*/
}


您可以#include ,然后直接从文件描述符STDIN_FILENO中读取。 dup2(pipeOne[0], stdin);也应该使用STDIN_FILENO,因为stdinFILE *而不是描述符。

不过,我认为您真的不想做任何事情。而不是从stdin进行读取,您应该将stdout挂钩到管道的写端(并将管道的下一个阶段连接到读取端),然后执行以开始管道的第一阶段。然后,被调用的子进程将从stdin中读取,转换输入并写入stdout,并用数据填充管道。


您可以使用很多不同的方法来完成此操作。使用fgets()读取数据,然后小心地write()写入管道可能就足够了:

1
2
3
4
5
6
7
8
9
10
11
char line[4096];
while (fgets(line, sizeof(line), stdin) != 0)
{
    size_t len = strlen(line);
    if (write(pipefd[1], line, len) != len)
    {
        fprintf(stderr,"Failed to write to pipe\
"
);
        exit(1);
    }
}

为了模拟管道,您实际上不需要将任何内容从标准输入复制到管道。您只需让rev读取标准输入即可。

这是从我对C Minishell的回答中快速得出的代码-添加满足您需求的管道。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/*
** How to write from stdin to a pipe in C
** https://stackoverflow.com/questions/19826211
**
** Write program pipeline to be invoked as:
**
**     pipeline -f outfile2 < infile > outfile1
**
** It should mimic the functionality of the bash script:
**
**     (rev | sort | uniq -c | tee outfile2 | wc) < infile > outfile1
**
** Refactored, with equivalent functionality:
**
**     rev < infile | sort | uniq -c | tee outfile2 | wc > outfile1
**
** Based on answer to SO 13636252 C Minishell adding pipelines
*/


/* pipeline.c */
#include
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

/*  who | awk '{print $1}' | sort | uniq -c | sort -n */
static char *cmd0[] = {"rev",        0 };
static char *cmd1[] = {"sort",       0 };
static char *cmd2[] = {"uniq","-c", 0 };
static char *cmd3[] = {"tee", 0,     0 };
static char *cmd4[] = {"wc",         0 };

static char **cmds[] = { cmd0, cmd1, cmd2, cmd3, cmd4 };
static int   ncmds = sizeof(cmds) / sizeof(cmds[0]);

static char const usestr[] ="[-f filename]";

typedef int Pipe[2];

/* These functions normally declared in stderr.h */
static void err_setarg0(const char *argv0);
static void err_sysexit(char const *fmt, ...);
static void err_syswarn(char const *fmt, ...);
static void err_usage(char const *usestr);

/* exec_nth_command() and exec_pipe_command() are mutually recursive */
static void exec_pipe_command(int ncmds, char ***cmds, Pipe output);

/* With the standard output plumbing sorted, execute Nth command */
static void exec_nth_command(int ncmds, char ***cmds)
{
    assert(ncmds >= 1);
    if (ncmds > 1)
    {
        pid_t pid;
        Pipe input;
        if (pipe(input) != 0)
            err_sysexit("Failed to create pipe");
        if ((pid = fork()) < 0)
            err_sysexit("Failed to fork");
        if (pid == 0)
        {
            /* Child */
            exec_pipe_command(ncmds-1, cmds, input);
        }
        /* Fix standard input to read end of pipe */
        dup2(input[0], 0);
        close(input[0]);
        close(input[1]);
    }
    execvp(cmds[ncmds-1][0], cmds[ncmds-1]);
    err_sysexit("Failed to exec %s", cmds[ncmds-1][0]);
    /*NOTREACHED*/
}

/* Given pipe, plumb it to standard output, then execute Nth command */
static void exec_pipe_command(int ncmds, char ***cmds, Pipe output)
{
    assert(ncmds >= 1);
    /* Fix stdout to write end of pipe */
    dup2(output[1], 1);
    close(output[0]);
    close(output[1]);
    exec_nth_command(ncmds, cmds);
}

/* Execute the N commands in the pipeline */
static void exec_pipeline(int ncmds, char ***cmds)
{
    assert(ncmds >= 1);
    pid_t pid;
    if ((pid = fork()) < 0)
        err_syswarn("Failed to fork");
    if (pid != 0)
        return;
    exec_nth_command(ncmds, cmds);
}

/* Collect dead children until there are none left */
static void corpse_collector(void)
{
    pid_t parent = getpid();
    pid_t corpse;
    int   status;
    while ((corpse = waitpid(-1, &status, 0)) != -1)
    {
        fprintf(stderr,"%d: child %d status 0x%.4X\
"
,
                (int)parent, (int)corpse, status);
    }
}

int main(int argc, char **argv)
{
    int opt;
    char *filename ="outfile2";    // Default file name

    err_setarg0(argv[0]);

    while ((opt = getopt(argc, argv,"f:")) != -1)
    {
        switch (opt)
        {
        case 'f':
            filename = optarg;
            break;
        default:
            err_usage(usestr);
            break;
        }
    }
    if (optind != argc)
        err_usage(usestr);

    /* Set the file name for tee to write to */
    cmd3[1] = filename;

    exec_pipeline(ncmds, cmds);
    corpse_collector();
    return(0);
}

/* Normally in stderr.c */
static const char *arg0 ="<undefined>";

static void err_setarg0(const char *argv0)
{
    arg0 = argv0;
}

static void err_usage(char const *usestr)
{
    fprintf(stderr,"Usage: %s %s\
"
, arg0, usestr);
    exit(1);
}

static void err_vsyswarn(char const *fmt, va_list args)
{
    int errnum = errno;
    fprintf(stderr,"%s:%d:", arg0, (int)getpid());
    vfprintf(stderr, fmt, args);
    if (errnum != 0)
        fprintf(stderr," (%d: %s)", errnum, strerror(errnum));
    putc('\
'
, stderr);
}

static void err_syswarn(char const *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    err_vsyswarn(fmt, args);
    va_end(args);
}

static void err_sysexit(char const *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    err_vsyswarn(fmt, args);
    va_end(args);
    exit(1);
}

输出示例(infile是程序源代码的副本时的outfile1):

1
 125     691    4879

输出示例(outfile2的前10行和后10行):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  22
   1 )... ,tmf* tsnoc rahc(nrawsys_rre diov citats
   1 )... ,tmf* tsnoc rahc(tixesys_rre diov citats
   1 )0 < ))(krof = dip(( fi    
   1 )0 < ))(krof = dip(( fi        
   1 )0 =! )tupni(epip( fi        
   1 )0 =! dip( fi    
   1 )0 =! munrre( fi    
   1 )0 == dip( fi        
   1 )0vgra* rahc tsnoc(0grates_rre diov citats
...
   1 >h.tressa< edulcni#
   1 C ni epip a ot nidts morf etirw ot woH **
   1 eman elif tluafeD //    ;"2eliftuo" = emanelif* rahc    
   1 senilepip gnidda llehsiniM C 25263631 OS ot rewsna no desaB **
  10 {
   3 {    
   2 {        
  10 }
   3 }    
   2 }

(我注意到,无论行是否反转,字数都是相同的,因此rev命令在管道中没有任何作用。)有一些诊断输出被捕获;这些输出将被捕获。您可以轻松地抑制它。