#include <stdio.h>
int main() {
printf("This goes to screen\n");
freopen("out.txt", "a", stdout);
printf("This goes to out.txt");
freopen("/dev/stdout", "a", stdout);
printf("This should go to screen too, but doesn't\n");
return 0;
}
J'appelle freopen pour rediriger le stdout out.txt, puis j'imprime quelque chose sur le fichier. Je veux maintenant le rediriger vers l'écran, mais freopen ("/ dev/stdout", "a", stdout); ne fonctionne pas. Est-il possible de le faire en utilisant des appels système ANSI C ou POSIX?
Je ne peux pas penser à un moyen de le faire de manière multiplateforme, mais sur les systèmes GNU/Linux (et peut-être même sur ceux compatibles POSIX), vous pouvez freopen ("/dev/tty", "a", stdout)
. Est-ce ce que vous essayiez de faire?
Malheureusement, il ne semble pas y avoir de bon moyen:
http://c-faq.com/stdio/undofreopen.html
La meilleure recommandation est de ne pas utiliser freopen dans ce cas.
Utilisez fdopen()
et dup()
ainsi que freopen()
.
int old_stdout = dup(1); // Preserve original file descriptor for stdout.
FILE *fp1 = freopen("out.txt", "w", stdout); // Open new stdout
...write to stdout... // Use new stdout
FILE *fp2 = fdopen(old_stdout, "w"); // Open old stdout as a stream
...Now, how to get stdout to refer to fp2?
...Under glibc, I believe you can use:
fclose(stdout); // Equivalent to fclose(fp1);
stdout = fp2; // Assign fp2 to stdout
// *stdout = *fp2; // Works on Solaris and MacOS X, might work elsewhere.
close(old_stdout); // Close the file descriptor so pipes work sanely
Je ne suis pas sûr que vous puissiez faire la mission de manière fiable ailleurs.
Le code ci-dessous a fonctionné sous Solaris 10 et MacOS X 10.6.2 - mais je ne suis pas sûr qu'il soit fiable. L'affectation de structure peut ou non fonctionner avec Linux glibc.
#include <stdio.h>
#include <unistd.h>
int main(void)
{
printf("This goes to screen\n");
int old_stdout = dup(1); // Consider dup(STDOUT_FILENO) or dup(fileno(stdout))
FILE *fp1 = freopen("out.txt", "a", stdout);
printf("This goes to out.txt\n");
fclose(stdout);
FILE *fp2 = fdopen(old_stdout, "w");
*stdout = *fp2; // Unreliable!
printf("This should go to screen too, but doesn't\n");
return 0;
}
Vous ne pouvez pas dire que vous n'étiez pas prévenu - c'est jouer avec le feu!
Si vous êtes sur un système avec le système de fichiers /dev/fd
, vous pouvez créer le nom du fichier impliqué par le descripteur de fichier renvoyé par dup()
avec sprintf(buffer, "/dev/fd/%d", old_stdout)
, puis utiliser freopen()
avec ce nom. Ce serait beaucoup plus fiable que l'affectation utilisée dans ce code.
Les meilleures solutions obligent le code à utiliser «fprintf (fp, ...)» partout ou bien une fonction de couverture qui vous permet de définir votre propre pointeur de fichier par défaut:
#include "mprintf.h"
#include <stdarg.h>
static FILE *default_fp = 0;
void set_default_stream(FILE *fp)
{
default_fp = fp;
}
int mprintf(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
if (default_fp == 0)
default_fp = stdout;
int rv = vfprintf(default_fp, fmt, args);
va_end(args);
return(rv);
}
#ifndef MPRINTF_H_INCLUDED
#define MPRINTF_H_INCLUDED
#include <stdio.h>
extern void set_default_stream(FILE *fp);
extern int mprintf(const char *fmt, ...);
#endif
Clairement, vous pouvez créer un mvprintf () et d’autres fonctions selon vos besoins.
Ensuite, au lieu du code original, vous pouvez utiliser:
#include "mprintf.h"
int main()
{
mprintf("This goes to screen\n");
FILE *fp1 = fopen("out.txt", "w");
set_default_stream(fp1);
mprintf("This goes to out.txt\n");
fclose(fp1);
set_default_stream(stdout);
mprintf("This should go to screen too, but doesn't\n");
return 0;
}
(Avertissement: code non testé - niveau de confiance trop élevé. En outre, tout le code écrit suppose que vous utilisiez un compilateur C99, principalement parce que je déclare des variables lorsque j'en ai besoin pour la première fois, pas au début de la fonction.)
Mise en garde:
Notez que si le programme d'origine est appelé en tant que ./original_program > file
ou ./original_program | grep something
(avec une sortie redirigée) ou est exécuté à partir d'un travail cron
, alors ouvrir /dev/tty
n'est généralement pas approprié pour rouvrir la sortie standard car la sortie standard d'origine n'était pas le terminal.
Notez également que si la redirection de la sortie standard est utilisée avant de forger et d'exécuter un programme enfant et que la sortie standard d'origine est rétablie dans le parent, la séquence d'opérations est incorrecte. Vous devez bifurquer puis ajuster les E/S de l'enfant (uniquement), sans aucune modification des E/S du parent.
De manière générale, vous ne pouvez pas. Vous avez fermé le dossier, qui aurait pu être un tuyau ou autre. Ce n'est pas rouvrable. Vous avez peut-être enregistré la valeur stdout
, puis lui attribuez un fopen, puis fermez-le et copiez l'ancienne valeur. Exemple:
FILE *o = stdout;
stdout=fopen("/tmp/crap.txt","a");
printf("Oh no!\n");
fclose(stdout);
stdout = o;
Mike Weller a suggéré dans les commentaires ci-dessous que stdout pourrait ne pas toujours être accessible en écriture. Dans ce cas, quelque chose comme ça pourrait aider:
int o = dup(fileno(stdout));
freopen("/tmp/crap.txt","a",stdout);
printf("Oh no!\n");
dup2(o,fileno(stdout));
close(o);
Une autre modification: si vous l'utilisez pour rediriger la sortie du processus enfant comme le suggère votre commentaire, vous pouvez le rediriger après le fork.
Sous Windows, vous pouvez ouvrir "CONOUT $".
freopen("test.txt", "w", stdout);
printf("this goes to test.txt");
freopen("CONOUT$", "w", stdout);
printf("this goes to the console\n");
Cela ne fonctionne probablement pas si stdout est redirigé pour commencer.
Le code suivant (SwapIOB) est utilisé dans les bancs d’essais qui veulent stocker Le flux stdout pour comparaison avec un fichier de résultats attendu.
Arrière-plan: les flux de fichiers sont gérés à l'aide d'une structure _IOB stockée dans un tableau de 20 entrées _IOB. Cela inclut le flux stdout. Les IOB sont stockés dans un tableau. Lorsqu'un fichier est créé, le code d'application obtient un ptr sur un élément de ce tableau. Le code d'application transmet ensuite ce ptr au système d'exploitation pour le traitement des appels d'E/S. Ainsi, le système d'exploitation ne contient pas lui-même ni ne s'appuie sur ses propres pointeurs vers l'IOB de l'application.
Condition préalable: lors de l'exécution d'un banc d'essai, les messages stdout émis par une application doivent être redirigés vers un fichier. Cependant, une fois le module testé terminé, les messages stdout doivent être redirigés vers la console.
Cette routine a été testée et est actuellement utilisée sur le système Windows XP/Pro.
void SwapIOB(FILE *A, FILE *B) {
FILE temp;
// make a copy of IOB A (usually this is "stdout")
memcpy(&temp, A, sizeof(struct _iobuf));
// copy IOB B to A's location, now any output
// sent to A is redirected thru B's IOB.
memcpy(A, B, sizeof(struct _iobuf));
// copy A into B, the swap is complete
memcpy(B, &temp, sizeof(struct _iobuf));
} // end SwapIOB;
Le code de l'application utilise SwapIOB () similaire à:
FILE *fp;
fp = fopen("X", "w");
SwapIOB(stdout, fp);
printf("text to file X");
SwapIOB(stdout, fp);
fclose(fp);
printf("text to console works, again!");