Escritura en stdin y lectura desde stdout (Programación UNIX/LINUX/C)

4 minutos de lectura

Estaba trabajando en una tarea en la que un programa tomaba un descriptor de archivo como argumento (generalmente del padre en una llamada ejecutiva) y leía de un archivo y escribía en un descriptor de archivo, y en mi prueba, me di cuenta de que el programa funcionaría desde la línea de comandos y no dar un error si usé 0, 1 o 2 como descriptor de archivo. Eso tenía sentido para mí, excepto que podía escribir en stdin y mostrarlo en la pantalla.

¿Hay alguna explicación para esto? Siempre pensé que había alguna protección en stdin/stdout y ciertamente no puedes fprintf a stdin o fgets de stdout.

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
    char message[20];
    read(STDOUT_FILENO, message, 20);
    write(STDIN_FILENO, message, 20);

    return 0;
}

  • Por favor, no confunda el archivo manejas con archivo descriptores. Es posible que no se le permita fprintf para stdin (aunque no puedo encontrar nada en el estándar que lo rechace explícitamente) pero eso no tiene ningún efecto sobre si puede write al descriptor de archivo 0.

    – pax diablo

    12 de septiembre de 2011 a las 6:09

  • ¿Cuál es exactamente la diferencia? ¿No están realizando la misma tarea?

    – Tim

    12 de septiembre de 2011 a las 6:13

  • Una zanahoria y un palo pueden lograr lo mismo tareay, sin embargo, no son lo mismo, solo pregúntele a su amigable burro vecino 🙂 Los identificadores son C estándar, los descriptores son POSIX.

    – pax diablo

    12 de septiembre de 2011 a las 6:44


Intentar escribir en un archivo marcado como de solo lectura o viceversa causaría write y read para devolver -1, y fallar. En esto específico case, stdin y stdout son en realidad el mismo archivo. En esencia, antes de que su programa se ejecute (si no realiza ninguna redirección), el shell dice:

  if(!fork()){
       <close all fd's>
       int fd = open("/dev/tty1", O_RDWR);
       dup(fd);
       dup(fd);
       execvp("name", argv);
  }

Por lo tanto, stdin, out y err son todos duplicados del mismo descriptor de archivo, abierto para lectura y escritura.

  • Corrección, esto no lo hace el shell sino el terminal (emulador). El shell heredará sus controladores stdin/stdout como cualquier otro proceso.

    – Vladimir Panteleev

    17 de noviembre de 2018 a las 8:05

read(STDIN_FILENO, message, 20); 
write(STDOUT_FILENO, message, 20);

Deberia trabajar. Nota: stdout puede ser un lugar diferente de stdin (incluso en la línea de comando). Puede alimentar la salida de otro proceso como stdin en su proceso, o organizar stdin/stdout para que sean archivos.

fprintf/fgets tiene un búfer, lo que reduce la cantidad de llamadas al sistema.

Mejor conjetura: stdin apunta de dónde proviene la entrada, su terminal y stdout apunta a dónde debería ir la salida, su terminal. Dado que ambos apuntan al mismo lugar, ¿son intercambiables (en este caso)?

  • Pero puede que no sean tu terminal.

    – Ed Heal

    12 de septiembre de 2011 a las 6:07

  • @Ed entonces leerá/escribirá desde/hacia lo que apunte stdin/stdout. Eso fue mucho de esto/aquello.

    – Avery3R

    12 de septiembre de 2011 a las 6:09

Si ejecuta un programa en UNIX

myapp < input > output

Puede abrir /proc/{pid}/fd/1 y leerlo, abrir /proc/{pid}/fd/0 y escribir en él y, por ejemplo, copiar output para input. (Posiblemente hay una forma más sencilla de hacer esto, pero sé que funciona)

Puedes hacer cualquier tipo de cosas que son simplemente confusas si te lo propones. 😉

Es muy posible que los descriptores de archivo 0, 1 y 2 estén abiertos tanto para lectura como para escritura (y, de hecho, todos se refieren a la misma “descripción de archivo abierto” subyacente), en cuyo caso lo que está haciendo funcionará. Pero que yo sepa, no hay garantía, por lo que es posible que tampoco funcione. Creo que POSIX en alguna parte especifica que si stderr está conectado a la terminal cuando el shell invoca un programa, se supone que se puede leer y escribir, pero no puedo encontrar la referencia de inmediato.

En general, recomendaría no leer nunca desde stdout o stderr a menos que esté buscando un terminal para leer una contraseña y stdin haya sido redirigido (no un tty). Y recomendaría nunca escribir en stdin: ¡es peligroso y podría terminar golpeando un archivo en el que el usuario no esperaba que se escribiera!

avatar de usuario
vinllen

Si desea escribir un mensaje en la entrada estándar, puede abrir el tty actual y llamar write llamada al sistema para escribir un mensaje en este fd:

string console_cmd = "hello";
string tty = ttyname(STDIN_FILENO);
int fd = open(tty.c_str(), O_WRONLY);
write(fd, console_cmd.c_str(), console_cmd.size());

Del mismo modo, lea desde stdout.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad