【问题标题】:How to write from stdin to a pipe in C如何从标准输入写入 C 中的管道
【发布时间】:2013-11-07 01:20:22
【问题描述】:

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

 pipeline -f outfile2 < infile > outfile1

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

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

我了解如何设置管道,但我不了解如何获取初始读数。我已经将 outfile2 文件名捕获到一个变量中,但我认为我可以保留外部两个,操作系统将分别将它们作为标准输入和标准输出。我如何以编程方式将标准输入读入父进程中的管道?

更新如下:以下代码不使用命令行参数修改输出文件:pipeline -f outfile2 outfile1
显然是真实的文件名。

主要:

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:

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*/
}

【问题讨论】:

  • 只是关于从管道stdin 读取的注释:如果管道的发送端发送\0,您将读取\0。这不常见,但可能发生。如果有人到达,它可能会到达fgets(...) 缓冲区的中间或scanf("%[^n]", ...) 缓冲区的中间。顺便说一句:我喜欢你的getc() 方法。
  • 你可以看看C Minishell — adding pipelines。除了更改命令并需要将参数插入tee 命令(这很容易)之外,您几乎可以从那里复制答案并完成工作。您不需要为rev 进程修复标准输入;您也不需要为wc 进程修复标准输出。 (该命令相当于rev &lt; infile1 | sort | uniq -c | tee outfile2 | wc &gt; outfile1

标签: c stdin pipe


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    有很多不同的方法可以做到;使用fgets() 读取数据可能就足够了,然后write() 小心地写入管道:

    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\n");
            exit(1);
        }
    }
    

    要模拟管道,您实际上不需要将任何内容从标准输入复制到管道;您可以简单地让rev 读取标准输入。

    这是从我对C Minishell — Adding Pipelines 的回答中快速得出的代码,可以满足您的需要。

    /*
    ** 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 <assert.h>
    #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\n",
                    (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\n", 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('\n', 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);
    }
    

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

     125     691    4879
    

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

      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 命令在管道中没有任何作用。)有一些诊断输出被捕获;你可以轻松压制它。

    【讨论】:

    • 上面更新了代码。我有类似的东西(但每个字符),它不起作用
    猜你喜欢
    • 2021-04-09
    • 1970-01-01
    • 1970-01-01
    • 2020-05-11
    • 2012-02-23
    • 1970-01-01
    • 2012-04-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多