【问题标题】:How to use dup and or dup2 to redirect standard out into a pipe, then out to antoher pipe and then back out to standard out?如何使用 dup 和/或 dup2 将标准输出重定向到管道,然后输出到另一个管道,然后返回到标准输出?
【发布时间】:2016-09-16 19:35:07
【问题描述】:

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

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

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

我觉得文件表周围有某种我不理解的层。

#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;
}

编辑********* 好的,我所知道的是,std out 以某种方式从 printf 之类的命令中获取信息,然后将其发送到缓冲区,然后刷新到 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。你在someInt 中拥有的是一个不同于1 调用后的描述符dup2 的描述符。 dup 系统调用确实重复描述符,它不使用引用或链接或类似的东西。此外,dup 调用不会修改您传递的描述符编号。在pipe 调用之后,pipeEnds_arr1[1] 不会随时更改值(描述符编号)。
  • 这无论如何都行不通。管道的一端用于写入,另一端用于读取。因此,您可以将stdout 替换为管道的写入端,并且您的程序可以将内容写入管道(认为它正在写入stdout)。但是管道的另一端不能直接连接到原来的stdout。有一段代码需要从管道中读取,并将数据写入原来的stdout
  • 这在理论上也行不通,因为它会创建一个无限循环。想想看 - 你最终会将 stdout 的输出反馈给它自己。
  • @user3386109 std out 以某种方式从 printf 等命令中获取信息,然后将其发送到缓冲区,然后刷新到 shell。必须有一种方法可以将管道的“读取”或输出端路由到同一个缓冲区,然后再到达外壳。 “顺便说一句,一些代码”是什么意思?我已经想出了如何将管道输出路由到一个字符串中,然后我可以随心所欲,但是我的理解中仍然缺少一些东西,因为似乎没有必要将输出路由到其他东西然后发送出去......?应该是一步。
  • @user6840486 我和你在 rici 的回答下交谈的人是同一个人。我正在等待您发布代码,显示您如何“在同一进程中通过管道相当轻松地将我的标准输出路由到文件”。将管道输出路由到字符串的代码也会很有趣。

标签: c pipe stdout dup2 dup


【解决方案1】:

管道不在文件描述符之间。它们在进程之间。所以“通过管道重新路由标准”是没有任何意义的。

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

stdout 不是某种魔法实体。它只是 fd 表中的条目 1,它可能指代任何 Unix 意义上的“文件”,包括常规文件、设备(包括控制台和您的 shell 与之通信的伪终端)、套接字、管道、FIFO 以及操作系统认为值得允许流式访问的任何其他内容。

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

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

【讨论】:

  • 我不太清楚你为什么说管道只能在不同的进程之间。在同一进程中,我可以相当轻松地将标准输出通过管道发送到文件。那么如何从标准输出馈送管道并将其用作缓冲区,然后将管道的输出端重定向回标准输出呢?我仍然不明白为什么这不能发生。我相当确定我将打印语句正确地路由到管道中,并且由于管道的行为就像一个缓冲区,我应该能够以某种方式将该缓冲区发送回控制台?
  • “我可以在同一进程中通过管道相当轻松地将标准输出路由到文件。” 如果您将该代码添加到问题 @user6840486 会有所帮助。
  • 对不起,我对此有点陌生。我应该如何添加此代码?我应该将其发布为编辑,还是将其添加到其他地方? @user3386109
  • “我应该将其发布为编辑” 是的,添加一段说明您在上面评论中所说的内容,然后在 @user6840486 之后添加代码
  • @user6840486:你坚持认为标准输出是一个东西。不是;它只是数字 1,用作打开“文件”数组的索引。管道是一种可能的文件。控制台是另一种可能性。如果当然,还有磁盘文件。等等。当您将管道复制到“stdout”时,您所做的实际上就是要求 io 库进行分配:fds[1] = fds[pipe]。管道实际上是两个文件,两端各有一个,因此您可以通过写入一端和读取另一端来使用它来传输数据。这真的与标准输出无关;除了 printf 总是写入 fd 1。
猜你喜欢
  • 2013-02-10
  • 1970-01-01
  • 1970-01-01
  • 2018-05-15
  • 2016-04-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多