web-dev-qa-db-fra.com

Les résultats de printf () et system () sont dans le mauvais ordre lorsque la sortie est redirigée vers un fichier

J'ai un programme C qui compile un exécutable appelé myprogram. C'est sa fonction principale:

int main(int argc, char ** argv) {
  printf("this is a test message.\n");
  system("ls");

  return 0;
}

Lorsque j'exécute myprogram > output.txt dans un shell Linux, puis que j'examine output.txt, la sortie de ls est répertoriée ci-dessus. Il s'agit d'un message de test.

Je pense que ce devrait être l'inverse. Pourquoi cela se produit-il et que puis-je faire pour que "ceci soit un message de test" apparaisse en haut du fichier output.txt?

Si cela compte, je suis nouveau dans C et travaille dans une ligne de commande.

97
Archr

Par défaut, stdout affiche line-buffered lorsqu’il est connecté à un terminal. Autrement dit, le tampon est vidé lorsqu'il est plein ou lorsque vous ajoutez une nouvelle ligne.

Cependant ​​, si stdout n'est pas connecté à un terminal, comme ce qui se produit lorsque vous redirigez la sortie de votre programme vers un fichier , alors stdout devient entièrement mis en mémoire tampon. Cela signifie que le tampon sera vidé et effectivement écrit, soit lorsqu'il sera plein, soit lorsqu'il sera explicitement vidé (ce qui se produit lorsque le programme se ferme).

Cela signifie que la sortie d'un processus séparé démarrant à partir de votre code (comme ce qui se passe lorsque vous appelez system) sera probablement écrite en premier, car la mémoire tampon de ce processus sera vidée à la fin du processus, ce qui est antérieur à votre propre processus.

Que se passe-t-il lors de l'utilisation de la redirection (ou des pipes d'ailleurs):

  1. Votre appel printf écrit dans la mémoire tampon stdout.
  2. La fonction system démarre un nouveau processus, qui écrit dans son propre tampon.
  3. Lorsque le processus externe (démarré par votre appel system) se termine, sa mémoire tampon est vidée et écrite. Votre propre tampon dans votre propre processus, n'est pas touché.
  4. Votre propre processus se termine et votre tampon stdout est vidé et écrit.

Pour obtenir la sortie dans l'ordre "correct" (ou du moins attendu), appelez fflush avant d'appeler system, pour vider explicitement stdout ou appelez setbuf avant toute sortie pour désactiver complètement la mise en mémoire tampon.

143

Il est lié à la mise en mémoire tampon de sortie. J'ai réussi à reproduire le même comportement. Forcer le flush l'a fait pour moi.

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char ** argv) {
  printf("this is a test message.\n");
  fflush(stdout);
  system("ls");

  return 0;
}

Avant d'ajouter le fflush:

$ ./main > foo
$ cat foo
main
main.c
this is a test message.

et après:

$ ./main > foo
$ cat foo
this is a test message.
foo
main
main.c
18
Aif

J'imagine que c'est à cause de l'ordre dans lequel le tampon stdout est vidé, ce qui n'est pas nécessairement déterministe. Il est possible que le parent engendre le processus ls et qu'il ne vide pas sa propre sortie standard avant son retour. En fait, il est possible que la vidange ne soit pas vidée avant la fin du processus.

Essayez d'ajouter fflush (stdout) après l'instruction printf et voyez si cela force la sortie à apparaître en premier.