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