77 votes

Reroutage de stdin et stdout de C

Je veux rouvrir le stdin y stdout (et peut-être stderr tant que j'y suis) les manipulations de fichiers, de sorte que les futurs appels à printf() o putchar() o puts() ira dans un fichier, et les futurs appels à getc() et autres viendront d'un fichier.

1) Je ne veux pas perdre de façon permanente les entrées/sorties/erreurs standard. Je peux vouloir les réutiliser plus tard dans le programme.

2) Je ne veux pas ouvrir de nouveaux filehandles car ces filehandles devraient être souvent transmis ou globaux (frissons).

3) Je ne veux utiliser aucune open() o fork() ou d'autres fonctions dépendantes du système si je ne peux pas m'en empêcher.

Donc, en gros, est-ce que ça marche de faire ça :

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

Et, si c'est le cas, comment puis-je obtenir la valeur originale de stdin retour ? Dois-je le stocker dans un FILE * et le récupérer plus tard ?

1 votes

Notez que stdin , stdout y stderr sont des variables globales.

97voto

bk1e Points 13737

Pourquoi utiliser freopen() ? La réponse à cette question se trouve dans l'une des notes de fin de chapitre de la spécification C89. <stdio.h> :

116. L'utilisation principale de la freopen consiste à modifier le fichier associé à un flux de texte standard ( standard ( stderr , stdin ou stdout ), car ces identifiants ne doivent pas nécessairement être valeurs modifiables auxquelles la valeur renvoyée par le fopen fonction peut être attribuée.

freopen est couramment utilisé à tort, par exemple stdin = freopen("newin", "r", stdin); . Ce n'est pas plus portable que fclose(stdin); stdin = fopen("newin", "r"); . Les deux expressions tentent d'attribuer à stdin dont la cessibilité n'est pas garantie.

La bonne façon d'utiliser freopen est d'omettre l'affectation : freopen("newin", "r", stdin);

2 votes

@AbiusX Je ne suis pas sûr de la portabilité de cette méthode, mais sous les environnements POSIX, vous pouvez le faire d'une autre manière - en changeant la signification du handle sous-jacent avec dup2() : linux.die.net/man/3/fflush . Cela vous permet de dupliquer le handle STDOUT avant de le remplacer, puis de le recopier. N'oubliez pas de fflush() . Il y a un exemple de cette utilisation de la fourchette ici : stackoverflow.com/questions/9405985/

0 votes

Pour ceux qui veulent fermer le descripteur standard avant de le rouvrir, il y a une solution testée dans msvc2015 UCRT, utiliser une séquence de fclose + _close + freopen : const int stdin_fileno = _fileno(stdin); fclose(stdin); if (stdin_fileno < 0) _close(STDIN_FILENO); /* may reallocate console here */; freopen("CONIN$", "r", stdin); /* use _get_osfhandle + SetStdHandle for not console application */ . Cela a du sens car freopen ne réutilise pas le descripteur déjà alloué et en alloue un nouveau. Pour le forcer à réutiliser le descripteur, vous devez donc le fermer d'abord.

17voto

John T Points 14067

Je pense que vous cherchez quelque chose comme freopen()

0 votes

Y a-t-il une différence entre stdin = freopen("newin", "r", stdin) ; et fclose(stdin) ; stdin = fopen("newin", "r") ; ou non ?

0 votes

Fopen ouvre un objet fichier tandis que freopen dirige votre flux de données vers l'objet fichier.de la page "Cette fonction est particulièrement utile pour rediriger des flux prédéfinis comme stdin, stdout et stderr vers des fichiers spécifiques".

1 votes

Existe-t-il un moyen de rouvrir stdin/stdout à leurs valeurs d'origine, autre que stdin = freopen("/dev/tty", "r") ; ou autre ?

11voto

C'est une version modifiée de la méthode de Tim Post ; j'ai utilisé /dev/tty au lieu de /dev/stdout. Je ne sais pas pourquoi cela ne fonctionne pas avec stdout (qui est un lien vers /proc/self/fd/1) :

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

En utilisant /dev/tty, la sortie est redirigée vers le terminal à partir duquel l'application a été lancée.

J'espère que ces informations vous seront utiles.

10voto

Tim Post Points 21270
freopen("/my/newstdin", "r", stdin);
freopen("/my/newstdout", "w", stdout);
freopen("/my/newstderr", "w", stderr);

... do your stuff

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

Cela fait grimper l'aiguille de mon round-peg-square-hole-o-meter, que cherchez-vous à accomplir ?

Edit :

Rappelez-vous que stdin, stdout et stderr sont les descripteurs de fichiers 0, 1 et 2 pour chaque processus nouvellement créé. freopen() devrait conserver les mêmes fd's, en leur assignant simplement de nouveaux flux.

Ainsi, une bonne façon de s'assurer que cela fait réellement ce que vous voulez que cela fasse serait :

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));

Je pense que c'est le comportement attendu de freopen(), comme vous pouvez le voir, vous n'utilisez toujours que trois descripteurs de fichiers (et les flux associés).

Cela annule toute redirection de l'interpréteur de commandes, car il n'y a rien à rediriger pour l'interpréteur de commandes. Cependant, cela va probablement casser les pipes. Vous devriez vous assurer de mettre en place un gestionnaire pour SIGPIPE, au cas où votre programme se trouverait à l'extrémité bloquante d'un pipe (pas FIFO, pipe).

Ainsi, ./votre_programme --stdout /tmp/stdout.txt --stderr /tmp/stderr.txt devrait être facilement réalisé avec freopen() et en gardant les mêmes descripteurs de fichiers réels. Ce que je ne comprends pas, c'est pourquoi il faudrait les remettre en place après les avoir modifiés ? Si quelqu'un a passé l'une ou l'autre option, il veut sûrement qu'elle persiste jusqu'à la fin du programme ?

0 votes

J'essaie d'ajouter un moyen pour mon programme de rediriger ses propres entrées et sorties standard, pour les personnes qui ne sont pas à l'aise avec la redirection de leur shell.

0 votes

Oui, freopen() sera votre ami pour ça.

0 votes

Cela ne fonctionne pas comme vous le décrivez. J'ai utilisé le code suivant : printf ("Ceci devrait être visible à l'écran ! \n ") ; freopen ("testop.txt", "w", stdout) ; printf ("Ceci devrait aller à testop ! \n ") ; fflush (stdout) ; fclose (stdout) ; freopen ("/dev/stdout", "w", stdout) ; printf ("Et ceci devrait encore aller à l'écran ! \n ") ;

9voto

gahooa Points 38006

La fonction os dup2() devrait fournir ce dont vous avez besoin (si ce ne sont pas des références à ce dont vous avez besoin).

Plus précisément, vous pouvez dup2() le descripteur de fichier stdin vers un autre descripteur de fichier, faire d'autres choses avec stdin, puis le recopier quand vous voulez.

La fonction dup() duplique un descripteur de fichier ouvert. Plus précisément, elle fournit une interface alternative au service fourni par la fonction fcntl() en utilisant la valeur de commande constante F_DUPFD, avec 0 pour son troisième argument. Le descripteur de fichier dupliqué partage tous les verrous avec l'original.

En cas de succès, dup() renvoie un nouveau descripteur de fichier qui a les éléments suivants en commun avec l'original :

  • Même fichier ouvert (ou pipe)
  • Même pointeur de fichier (les deux descripteurs de fichiers partagent un pointeur de fichier)
  • Même mode d'accès (lecture, écriture, ou lecture/écriture)

1 votes

La question dit explicitement qu'il ne faut pas utiliser les fonctions dépendantes du système, cependant.

0 votes

Et la question dit explicitement de limiter le nombre de flux ouverts / descripteurs de fichiers.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X