如何使用dup和/ dup2将标准重定向到管道,然后输出到antoher管道,然后退出标准输出?

时间:2016-09-16 19:35:07

标签: c pipe stdout dup2 dup

好的,有十亿个与dup,dup2,fcntl,pipe和各种各样的东西有关的演示,当存在多个进程时很棒。但是,我还没有看到一个非常基本的东西,我认为这将有助于解释管道的行为及其与标准输出的关系。

我的目标是简单地(在同一过程中)通过管道将标准输出重新路由回标准输出。我已经完成了这个 使用中间阶段将管道输出重定向到文件或写入缓冲区......然后将标准输出放回到它开始的位置。那时,我当然可以将缓冲区写回stdout,但我不想这样做。

由于我将标准输出移动到文件表中的另一个位置,我想直接将管道的输出直接送入新的标准输出位置并让它像通常那样打印。

我觉得文件表周围有一些我不理解的层。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

int main() {
    int pipeEnds_arr1[2];

    char str1[] = "STRING TO FEED INTO PIPE \n"; // make a string array

    pipe(pipeEnds_arr1);

    printf("File Descriptor for pipe ends from array\nPOSITION out  0 : %d\nPOSITION in 1 : %d\n", pipeEnds_arr1[0], pipeEnds_arr1[1]);


    /* now my goal is to shift the input of the pipe into the position of 
     * standard output, so that the print command feeds the pipe, then I 
     * would like to redirect the other end of the pipe to standard out. 
     */

    int someInt = dup(1); // duplicates stdout to next available file table position

    printf ("Some Int FD: %d\n", someInt); // print out the fd for someInt just for knowing where it is

    /* This is the problem area.  The out end of the pipe never 
     * makes it back to std out, and I see no way to do so.  
     * Stdout should be in the file table position 5, but when 
     * I dup2 the output end of the pipe into this position , 
     * I believe I am actually overwriting std out completely.  
     * But I don't want to overwrite it, i want to feed the output
     * of the pipe into std out. I think I am fundamentally 
     * misunderstanding this issue. 
     */

    dup2(pipeEnds_arr1[1], 1); //put input end of pipe into std out position
    //dup2(pipeEnds_arr1[0], 5); // this will not work
    //and other tests I have conducted do not work


    printf("File Descriptor for pipe ends from array\nPOSITION out  0 : %d\nPOSITION in 1 : %d\n", pipeEnds_arr1[0], pipeEnds_arr1[1]);

    fflush(stdout);

    close(pipeEnds_arr1[0]);
    close(pipeEnds_arr1[1]);

    return 0;
}

EDIT ********* 好吧,我所知道的是,std out会从printf等命令中获取信息,然后将其路由到缓冲区,然后刷新到shell中。

我认为必须有一种方法可以将管道的“读取”或输出端路由到同一个缓冲区然后到达shell。我已经想出如何将管道输出路由到一个字符串,然后我可以随意做。在下面发布的示例代码中,我将首先将管道输出到一个字符串,然后打开一个文件并将该字符串写入该文件的打开文件描述符...

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>


int main() {
    /*  Each pipe end array has to have 2 positions in it.  The array
     *  position represents the two pipe ends with the 0 index
     *  position representing the output of the pipe (the place you want
     *  read your data from), and 1 index position representing the
     *  input file descriptor of the pipe (the place you want to write
     *  your data).
     */
    int pipeEnds_arr1[2];

    char str1[] = "Hello, we are feeding this into the pipe that we are through stdout into a pipe and then reading from the pipe and then feeding that output into a file \n"; // make a string array

    /*  Here we want to actually do the pipe command. We feed it the array
     *  with the 2 positions in it which will now hold file descriptors
     *  attached to the current process which allow for input and output
     *  through the new pipe. At this point, we don't know what the
     *  exact file decriptors are, but we can look at them by printing
     */

    pipe(pipeEnds_arr1);
    printf("File Descriptor for pipe ends from array\nPOSITION out  0 : %d\nPOSITION in 1 : %d\n", pipeEnds_arr1[0], pipeEnds_arr1[1]);

    /* now my goal is to shift the input of the pipe into the position of 
     * standard output, so that the print command feeds the pipe, then we
     * will try to read from the pipe and redirect the output to the std 
     * or in this test case out to a file.
     */

    int someInt = dup(1); // we moved what was stdout into someInt;

    /* put the write end of the pipe in the old stdout position by
     * using dup2 so we will print directly into the pipe
     */
    dup2(pipeEnds_arr1[1], 1);

    /* this is where id like to re-rout the pipe back to stdout but
     * im obviously not understanding this correctly
     */
    //dup2(someInt, 3);

    /* since std out has now been replaced by the pipe write end, this
     * printf will print into the pipe
     */
    printf("%s", str1);


    /* now we read from the pipe into a new string we make */
    int n;
    char str2[strlen(str1)];
    n = read(pipeEnds_arr1[0], str2, sizeof(str2)-1);
    str2[n] = 0;

    /* open a file and then write into it from the output of the pipe 
     * that we saved into the str2
     */
    int fd = open("tmp.out", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    write(fd, str2, strlen(str2));


    /* not sure about these last commands and their relevance */
    fflush(stdout);
    close(pipeEnds_arr1[0]);
    close(pipeEnds_arr1[1]);
    close(fd);

    return 0;
}

1 个答案:

答案 0 :(得分:2)

管道不在文件描述符之间。它们介于进程之间。因此,“通过管道重新路由标准”没有任何意义。

您可以做的是修改进程的文件描述符表,使其stdout(fd 1)成为管道的写入端。并且您可以修改另一个进程的文件描述符表,以便某些文件描述符,甚至stdin(fd 0)是同一管道的读取端。这允许您通过两个进程之间的管道传递数据。 (如果你愿意,你可以在同一个进程中在两个fds之间设置一个管道;它偶尔会有用,但要注意死锁。)

stdout不是某种神奇的实体。它只是fd表中的入口1,它可能引用任何“文件”,在Unix的意义上,包括常规文件,设备(包括控制台和shell正在与之通信的伪终端),套接字,管道,FIFO以及操作系统认为值得允许流式访问的其他任何东西。

通常,当shell启动运行命令行实用程序时,它首先从自己的fd 0,1和2克隆fds 0,1和2(stdin,stdout和stderr),它们通常都是相同的设备:控制台,或者更常见的是,您正在使用的图形控制台应用程序提供的伪终端。但是您可以使用shell重定向运算符,shell管道运算符和一些shell提供的特殊文件来更改这些赋值。

最后,管道在内核中确实有小缓冲区,但关键是“小”字 - 缓冲区可能只有4096字节。如果变满,则尝试写入管道将一直挂起,直到空间可用,这只有在从另一个sude读取数据时才会发生。这就是为什么如果相同的进程使用管道的两侧就很容易死锁:如果进程挂起等待堆栈被清空,那么wikk永远无法读取管道。