He escrito un programa simple en C++ con fines de tutorial. Mi objetivo es repetirlo infinitamente.
#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; } Después de la compilación, lo ejecuto así: ./bin 0>&1 Lo que esperaba que sucediera es que el "texto" que se envía a stdout, ahora también se convertirá en stdin para el programa y se repetirá para siempre. ¿Por qué no sucede?
Primero, debe generar líneas nuevas al imprimir en std::cout , de lo contrario, std::getline() no tendrá ninguna línea completa para leer.
Versión mejorada:
#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; }Ahora prueba esto:
./bin >file <fileno ve ninguna salida, porque va al archivo. Pero si detienes el programa y miras el archivo, mira, está lleno de
stars stars stars stars:-)
Además, la razón por la que el ciclo de retroalimentación no puede comenzar cuando intenta
./bin 0>&1 es que terminas con stdin y stdout conectados a /dev/tty (lo que significa que puedes ver la salida).
Pero un dispositivo TTY nunca puede cerrar el ciclo, porque en realidad consta de dos canales separados, uno que pasa la salida a la terminal y otro que pasa la entrada de la terminal al proceso.
Si usa un archivo regular para entrada y salida, el bucle se puede cerrar. Cada byte escrito en el archivo también se leerá, si el stdin del proceso está conectado a él. Eso es siempre que ningún otro proceso lea el archivo simultáneamente, porque cada byte en una secuencia solo se puede leer una vez.
Porque stdout y stdin no crean un bucle. Pueden apuntar al mismo tty, pero un tty en realidad son dos canales separados, uno para entrada y otro para salida, y no se enlazan entre sí.
Puede intentar crear un bucle ejecutando su programa con su stdin conectado al extremo de lectura de una tubería y con su stdout al extremo de escritura. Eso funcionará con 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 ... Pero no con su programa . Eso es porque su programa está tratando de leer líneas , pero sus escrituras no terminan con una nueva línea. Arreglando eso y también imprimiendo la línea de lectura en stderr (para que no tengamos que usar strace para demostrar que sucede algo en su programa), obtenemos:
#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 ... Nota: la forma <>fifo de abrir una canalización con nombre (fifo) se usó para abrir tanto el extremo de lectura como el de escritura a la vez y así evitar el bloqueo. En lugar de reabrir la fifo desde su ruta, la salida estándar podría simplemente duplicarse desde la entrada estándar ( prog <>fifo >&0 ) o la fifo podría abrirse primero como un descriptor de archivo diferente, y luego podrían abrirse la entrada estándar y la salida estándar sin bloqueo, el primero en modo de solo lectura y el segundo en modo de solo escritura ( prog 3<>fifo <fifo >fifo 3>&- ).
Todos funcionarán igual con el ejemplo en cuestión. En Linux, :|prog >/dev/fd/0 (y echo text | strace cat >/dev/fd/0 ) también funcionaría, sin tener que crear una canalización con nombre con mkfifo .
Como está usando gcc, supondré que tiene una pipe disponible.
#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; } La sección #if 0 se puede habilitar para obtener la depuración. No pude hacer que funcionara con std::cout y std::cin directamente, pero alguien que sepa más sobre el código de flujo de bajo nivel probablemente podría modificar esto.
Salida de depuración:
$ ./io_loop test Got 4 bytes test Got 4 bytes test Got 4 bytes test Got 4 bytes ^C