如何将程序输出重定向为输入

How to redirect program output as its input

我已经编写了一个简单的 C++ 程序用于教程目的。 我的目标是无限循环。

#include <iostream>
#include <string>

int main()
{
  std::cout << "text";

  for(;;) {
    std::string string_object{};
    std::getline(std::cin, string_object);
    std::cout << string_object;
  }

  return 0;
}

编译后我运行它是这样的: ./bin 0>&1 我期望发生的是输出到标准输出的“文本”,它现在也将成为程序的标准输入,并且它将永远循环。为什么没有发生?

首先,打印到std::cout时需要输出换行符,否则std::getline()将没有完整的行可读。

改进版本:

#include <iostream>
#include <string>

int main()
{
  std::cout << "stars" << std::endl;

  for(;;) {
    std::string string_object;
    std::getline(std::cin, string_object);
    std::cout << string_object << std::endl;
  }

  return 0;
}

现在试试这个:

./bin >file <file

您看不到任何输出,因为它会转到文件。但是如果你停止程序并查看文件,看,它充满了

stars
stars
stars
stars

:-)

还有,当你尝试时反馈循环无法启动的原因

./bin 0>&1

是,您最终将标准输入和标准输出都连接到 /dev/tty (这意味着您可以看到输出)。

但是 TTY 设备永远无法关闭循环,因为它实际上由两个独立的通道组成,一个将输出传递给终端,一个将终端输入传递给进程。

如果您使用常规文件进行输入和输出,则可以关闭循环。如果进程的标准输入连接到它,写入文件的每个字节也将从中读取。只要没有其他进程同时从文件读取,因为流中的每个字节只能读取一次。

因为标准输出和标准输入不创建循环。它们可能指向同一个 tty,但一个 tty 实际上是两个独立的通道,一个用于输入,一个用于输出,并且它们不会相互环回。

您可以尝试通过 运行 您的程序创建一个循环,其 stdin 连接到 pipe 的读取端,并将其 stdout 连接到其写入端。这将适用于 cat:

mkfifo fifo
{ echo text; strace cat; } <>fifo >fifo
...
read(0, "text\n", 131072)               = 5
write(1, "text\n", 5)                   = 5
read(0, "text\n", 131072)               = 5
write(1, "text\n", 5)                   = 5
...

但是你的程序没有。那是因为您的程序正在尝试读取 ,但它的写入没有被换行符终止。修复该问题并将读取行打印到 stderr(因此我们不必使用 strace 来证明您的程序中发生了任何事情),我们得到:

#include <iostream>
#include <string>

int main()
{
  std::cout << "text" << std::endl;

  for(;;) {
    std::string string_object{};
    std::getline(std::cin, string_object);
    std::cerr << string_object << std::endl;
    std::cout << string_object << std::endl;
  }
}
g++ foo.cc -o foo
mkfifo fifo; ./foo <>fifo >fifo
text
text
text
...

注意:使用<>fifo打开命名管道(fifo)的方式是为了同时打开它的读端和写端,因此避免阻塞。不是从它的路径重新打开 fifo,stdout 可以简单地从 stdin (prog <>fifo >&0) 复制,或者 fifo 可以首先作为不同的文件描述符打开,然后可以打开 stdin 和 stdout 而无需阻塞,第一个在 read-only 模式下,第二个在 write-only 模式下 (prog 3<>fifo <fifo >fifo 3>&-).

对于手头的例子,它们的工作方式都是一样的。在 Linux、:|prog >/dev/fd/0(和 echo text | strace cat >/dev/fd/0)上也可以工作——无需使用 mkfifo.

创建命名管道

由于您使用的是 gcc,我假设您有 pipe 可用。

#include <cstring>
#include <iostream>
#include <unistd.h>

int main() {
    char buffer[1024];
    std::strcpy(buffer, "test");

    int fd[2];
    ::pipe(fd);
    ::dup2(fd[1], STDOUT_FILENO);
    ::close(fd[1]);
    ::dup2(fd[0], STDIN_FILENO);
    ::close(fd[0]);

    ::write(STDOUT_FILENO, buffer, 4);
    while(true) {
        auto const read_bytes = ::read(STDIN_FILENO, buffer, 1024);
        ::write(STDOUT_FILENO, buffer, read_bytes);
#if 0
        std::cerr.write(buffer, read_bytes);
        std::cerr << "\n\tGot " << read_bytes << " bytes" << std::endl;
#endif
        sleep(2);
    }
    return 0;
}

可以启用 #if 0 部分进行调试。我无法让它直接与 std::coutstd::cin 一起使用,但对 low-level 流代码了解更多的人可能会对此进行调整。

调试输出:

$ ./io_loop 
test
        Got 4 bytes
test
        Got 4 bytes
test
        Got 4 bytes
test
        Got 4 bytes
^C