2009-02-25 22 views
51

quiero volver a abrir el stdin y stdout (y tal vez stderr mientras estoy en ello) filehandles, para que las futuras llamadas a printf() o putchar()puts() o irán a un archivo, y futuras llamadas a getc() y tal procederá de un archivo.Reencaminamiento entrada y salida estándar de C

1) No quiero perder de forma permanente de entrada/salida/error estándar. Es posible que desee reutilizarlos más adelante en el programa.

2) No quiero abrir nuevos controladores de archivo debido a que estos controladores de archivo tendrían que ser aprobado ya sea alrededor de un lote o global (estremecimiento).

3) No deseo utilizar ninguna open() o fork() u otras funciones dependientes del sistema si no puedo evitarlo.

Así que, básicamente, funciona para hacer esto:

stdin = fopen("newin", "r"); 

Y, si lo hace, ¿cómo puedo obtener el valor original de stdin espalda? ¿Debo guardarlo en un FILE * y recuperarlo más tarde?

+0

Tenga en cuenta que 'stdin',' stdout', y 'stderr' son variables globales. –

Respuesta

69

¿Por qué utilizar freopen()?La especificación C89 tiene la respuesta en una de las notas al final de la sección sobre <stdio.h>:

116. El principal uso de la función freopen es cambiar el archivo asociado a una corriente texto estándar (stderr , stdin, o stdout), ya que esos identificadores no necesitan ser lvalues ​​modificables a la que el valor devuelto por el fopen Puede asignarse la función .

freopen es comúnmente utilizado indebidamente, p. stdin = freopen("newin", "r", stdin);. Esto no es más portátil que fclose(stdin); stdin = fopen("newin", "r");. Ambas expresiones intentan asignar a stdin, que no se garantiza que sea asignable.

La forma correcta de utilizar freopen es omitir la misión: freopen("newin", "r", stdin);

+9

cómo restaurarlo, entonces? – AbiusX

+3

http://c-faq.com/stdio/undofreopen.html –

+0

@AbiusX No estoy seguro de lo portátil que es, pero en entornos POSIX puedes hacerlo de otra forma: cambiando el significado del identificador subyacente con 'dup2() ': http://linux.die.net/man/3/fflush. Esto le permitirá duplicar el controlador STDOUT antes de reemplazarlo, luego puede copiar de nuevo. Solo recuerde 'fflush()'. Hay un ejemplo de esto que se usa para bifurcar aquí: http://stackoverflow.com/questions/9405985/linux-3-0-executing-child-process-with-piped-stdin-stdout –

14

creo que estés buscando algo como freopen()

+0

¿Hay alguna diferencia entre stdin = freopen ("newin", "r", stdin); y fclose (stdin); stdin = fopen ("newin", "r"); ¿o no? –

+0

fopen abre un objeto de archivo mientras freopen apunta a su flujo de datos en el objeto de archivo.Desde la página "Esta función es especialmente útil para redireccionar secuencias predefinidas como stdin, stdout y stderr a archivos específicos" –

+1

¿Hay alguna forma de volver a abrir stdin/stdout a sus valores originales que no sean stdin = freopen ("/ dev/tty", "r"); ¿o algo? –

8

La función OS dup2() debe proporcionar lo que necesita (si no referencias a exactamente lo que necesita).

Más específicamente, se puede dup2() el descriptor de archivo de la entrada estándar a otro descriptor de archivo, hacer otras cosas con la entrada estándar, y luego copiar de nuevo cuando lo desee.

La función dup() duplica un descriptor de archivo abierto. Específicamente, proporciona una interfaz alternativa al servicio proporcionado por la función fcntl() utilizando el valor de comando constante F_DUPFD, con 0 para su tercer argumento. El descriptor de archivo duplicado comparte cualquier bloqueo con el original.

En caso de éxito, dup() devuelve un nuevo descriptor de archivo que tiene el siguiente en común con el original:

  • mismo archivo abierto (o tubo)
  • puntero del archivo mismo (ambos descriptores de archivos comparten una apuntador de archivo)
  • mismo modo de acceso (lectura, escritura o lectura/escritura)
+0

Sin embargo, la pregunta explícitamente dice que no se deben usar funciones dependientes del sistema. – unwind

+0

Y la pregunta explícitamente dice limitar el número de streams abiertos/descriptores de archivos –

3

freopen resuelve la parte fácil. Mantener la entrada estándar de edad alrededor no es difícil si usted no ha leído nada y si usted está dispuesto a utilizar el sistema POSIX llama como dup o dup2. Si comenzó a leer, todas las apuestas están desactivadas.

Tal vez usted puede decirnos el contexto en que se produce este problema?

Lo animo a que se adhiera a las situaciones en las que está dispuesto a abandonar el antiguo stdin y stdout y por lo tanto puede usar freopen.

+0

En contexto, creo que podría salirse con la suya abandonando el viejo stdin/stdout si realmente tuviera que hacerlo. Estoy intentando agregar a mi programa una opción para que el programa redireccione stdin y stdout, para personas que están ejecutando el programa y que no conocen muy bien su caparazón. Sólo por el placer de hacerlo. –

+1

Mis alumnos definitivamente tienen problemas con la redirección de E/S, por lo que su objetivo parece ser uno bueno. ¿Tal vez sería mejor con las opciones de línea de comandos -i y -o para nombrar los archivos de entrada y salida? –

+0

Ese es mi plan más o menos (-i y -o ya están tomados, pero esa es la interfaz general), solo necesitaba una forma de implementarlo (sin tener que cambiar todos mis printf() s a fprintf() sy pasar alrededor de dos manejadores de archivos de todos modos). Además, una bandera para el reencaminamiento stderr sería agradable. –

8
freopen("/my/newstdin", "r", stdin); 
freopen("/my/newstdout", "w", stdout); 
freopen("/my/newstderr", "w", stderr); 

... do your stuff 

freopen("/dev/stdin", "r", stdin); 
... 
... 

Este picos de la aguja en mi ida y PEG-cuadrado-agujero-o-meter, ¿qué estás tratando de lograr?

Editar:

Recuerde que la entrada estándar, stdout y stderr son descriptores de archivo 0, 1 y 2 para cada proceso de nueva creación. freopen() debería mantener los mismos fd, solo asignarles nuevas transmisiones.

Por lo tanto, una buena manera de asegurar que esto está haciendo realidad lo que quiere que haga sería:

printf("Stdout is descriptor %d\n", fileno(stdout)); 
freopen("/tmp/newstdout", "w", stdout); 
printf("Stdout is now /tmp/newstdout and hopefully still fd %d\n", 
    fileno(stdout)); 
freopen("/dev/stdout", "w", stdout); 
printf("Now we put it back, hopefully its still fd %d\n", 
    fileno(stdout)); 

Creo que este es el comportamiento esperado de freopen(), como se puede ver, Todavía estoy usando solo tres descriptores de archivos (y flujos asociados).

Esto anularía cualquier redirección de shell, ya que no habría nada para que el shell redirigiera. Sin embargo, es probable que rompa las tuberías. Es posible que desee asegurarse de configurar un controlador para SIGPIPE, en caso de que su programa se encuentre en el extremo de bloqueo de una tubería (no FIFO, tubería).

Por lo tanto, ./your_program --stdout /tmp/stdout.txt --stderr /tmp/stderr.txt se debe realizar fácilmente con freopen() y manteniendo los mismos descriptores de archivo reales. Lo que no entiendo es por qué tendrías que volver a ponerlos una vez que los cambies? Seguramente, si alguien aprobó cualquiera de las opciones, querrían que persistiera hasta que el programa terminara?

+0

Estoy intentando agregar una forma para que mi programa redirija su propia entrada y salida estándar, para las personas que no se sienten cómodas con la redirección de su caparazón. –

+0

Sí, freopen() va a ser tu amigo para eso. –

+0

Esto no funciona como lo describes. Utilicé el siguiente código: printf ("¡Esto debería estar visible en la pantalla! \ N"); freopen ("testop.txt", "w", stdout); printf ("Esto debería ir a testop!\ n "); fflush (stdout); fclose (stdout); freopen ("/dev/stdout "," w ", stdout); printf (" ¡Y esto debería volver a la pantalla! \ n "); – VectorVictor

9

Esta es una versión modificada del método de Tim Post; Usé/dev/tty en lugar de/dev/stdout. No sé por qué no funciona con la salida estándar (que es un enlace a/proc/self/fd/1):

freopen("log.txt","w",stdout); 
... 
... 
freopen("/dev/tty","w",stdout); 

Mediante el uso de/dev/tty la salida se redirige al terminal desde donde se lanzó la aplicación.

Espero que esta información sea útil.

3

Y mientras tanto, hay una biblioteca de código fuente C que hará todo esto por usted, redireccionando stdout o stderr. Pero lo mejor es que le permite asignar tantas funciones de devolución de llamada como desee a las transmisiones interceptadas, lo que le permite enviar fácilmente un solo mensaje a múltiples destinos, una base de datos, un archivo de texto, etc.

activado Además, hace que sea trivial crear nuevas secuencias que se vean y se comporten de la misma manera que stdout y stderr, donde también se pueden redirigir estas nuevas transmisiones a varias ubicaciones.

busque la biblioteca C de U-Streams en * oogle.

0

este es el moste avalaible Handly y forma útil

freopen("dir","r",stdin); 
Cuestiones relacionadas