Ergebnisse von printf() und system() sind in der falschen Reihenfolge, wenn die Ausgabe in eine Datei umgeleitet wird [duplicate]

Lesezeit: 4 Minuten

Benutzeravatar von Archr
Archr

Ich habe ein C-Programm, das zu einer ausführbaren Datei namens myprogram kompiliert wird. Dies ist seine Hauptfunktion:

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

  return 0;
}

Wenn ich laufe myprogram > output.txt in einer Linux-Shell und untersuche dann output.txt, ich sehe die Ausgabe von ls oben aufgeführt “Dies ist eine Testnachricht.”

Ich habe das Gefühl, dass es umgekehrt sein sollte. Warum passiert das und was kann ich tun, damit „Dies ist eine Testnachricht“ oben in der Ausgabe.txt angezeigt wird?

Wenn es darauf ankommt, ich bin neu in C und arbeite in einer Befehlszeile.

  • Wenn Sie verschiedene Ausgabe- und Eingabepuffer mischen, sich aber nicht die Mühe machen, Ihre Ausgabe zu leeren, gibt es keine Garantie dafür, in welcher Reihenfolge sie geschrieben werden. Dein printf Ausgabe könnte vor, nach oder sogar in der Mitte ausgeschrieben werden ls Ausgang! Zugegeben, es ist selten, dass eine Ausgabe mitten in eine andere Ausgabe geschrieben wird, aber ich habe es erlebt. Intuitiv denken Sie: “Es gibt keine Möglichkeit, dass eine Ausgabezeile mitten in eine andere geschrieben wird”, aber wenn Sie es tatsächlich sehen, wird Ihre Wahrnehmung der Realität herausgefordert.

    – J L

    27. September 2018 um 17:33 Uhr

  • Sie müssen spülen, damit gedruckte Sachen sofort gedruckt werden.

    – Mathematiker

    29. September 2018 um 11:32 Uhr


Standardmäßig ausgegeben an stdout ist zeilengepuffert bei Anschluss an ein Terminal. Das heißt, der Puffer wird geleert, wenn er voll ist oder wenn Sie einen Zeilenumbruch hinzufügen.

Jedochwenn stdout nicht mit einem Terminal verbunden ist, was dann passiert, wenn Sie die Ausgabe Ihres Programms in eine Datei umleiten stdout wird vollständig gepuffert. Das bedeutet, dass der Puffer geleert und tatsächlich geschrieben wird, entweder wenn er voll ist oder wenn er explizit geleert wird (was passiert, wenn das Programm beendet wird).

Dies bedeutet, dass die Ausgabe eines separaten Prozesses von Ihrem Code aus gestartet wurde (wie das, was passiert, wenn Sie aufrufen system) wird höchstwahrscheinlich zuerst geschrieben, da der Puffer dieses Prozesses geleert wird, wenn dieser Prozess endet, was vor Ihrem eigenen Prozess liegt.

Was passiert bei der Verwendung der Umleitung (oder Pipes für diese Angelegenheit):

  1. Dein printf Anruf schreibt an die stdout Puffer.
  2. Das system Funktion startet einen neuen Prozess, der in seinen eigenen Puffer schreibt.
  3. Wenn der externe Prozess (gestartet von Ihrer system call) beendet, wird sein Puffer geleert und geschrieben. Ihr eigener Puffer in Ihrem eigenen Prozess wird nicht berührt.
  4. Ihr eigener Prozess endet, und Ihr stdout Puffer wird geleert und geschrieben.

Rufen Sie an, um die Ausgabe in der “richtigen” (oder zumindest erwarteten) Reihenfolge zu erhalten fflush vor dem Anruf systemum explizit zu spülen stdoutoder anrufen setbuf vor jeder Ausgabe, um die Pufferung vollständig zu deaktivieren.

  • Aus Neugier: Gibt das Programm aus korrekt selbst wenn fflush wird nach gerufen systemoder der system macht alles (einschließlich seiner eigenen fflush ) in diesem Anruf?

    – ähmaan

    27. September 2018 um 14:46 Uhr


  • Eine weitere merkwürdige Frage – nicht die Ausgabe von system() landen auch im selben stdout Puffer das printf() vorher benutzt? Immerhin hat es es nicht umgangen und direkt auf die Konsole geschrieben. Das stdouts beider Programme verkettet wurden – wie die Tatsache zeigt, dass beide in endeten output.txt.

    – Vix-

    27. September 2018 um 17:18 Uhr


  • @Vilx-, nein, stdio eine Bibliothek ist, existieren die verwendeten Puffer im Speicher des Prozesses. system() startet einen neuen Prozess mit einem separaten Speicherplatz und daher separaten Puffern. Der untergeordnete Prozess erbt jedoch den Dateideskriptor vom übergeordneten Prozess, weshalb die Ausgabe in dieselbe Datei geht. Der Unterschied zwischen den Dateideskriptoren auf Betriebssystemebene und der write() Systemaufruf; und die C-Bibliothek FILE Ströme und printf() (etc.) ist hier wichtig.

    – Ilkkachu

    27. September 2018 um 17:27 Uhr

  • Ihre Antwort beschönigt ein Detail, das dem OP vielleicht bekannt sein sollte: The stdout Variable im C-Programm enthält einen Zeiger auf eine libc FILE Objekt, aber was von den beiden Prozessen geteilt wird, ist ein offenes Dateideskriptor.

    – beschmutzt

    27. September 2018 um 18:53 Uhr

Es hängt mit der Ausgabepufferung zusammen. Ich konnte das gleiche Verhalten reproduzieren. Das Erzwingen der Spülung hat es für mich getan.

#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;
}

Vor dem Hinzufügen der Spülung:

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

und danach:

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

Benutzeravatar von ConcernedOfTunbridgeWells
ConcernedOfTunbridgeWells

Ich vermute, es liegt an der Reihenfolge, in der der stdout-Puffer geleert wird, was nicht unbedingt deterministisch ist. Es ist möglich, dass der Elternteil die spawnt ls Prozess und löscht seine eigene stdout erst, nachdem diese zurückkehrt. Es kann sein, dass stdout nicht wirklich geleert wird, bis der Prozess beendet wird.

Versuchen Sie, hinzuzufügen fflush (stdout) nach der printf-Anweisung und prüfen Sie, ob dies die Ausgabe zuerst erzwingt.

1421020cookie-checkErgebnisse von printf() und system() sind in der falschen Reihenfolge, wenn die Ausgabe in eine Datei umgeleitet wird [duplicate]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy