Umleitung von stdin und stdout von C

Lesezeit: 7 Minuten

Ich möchte die neu eröffnen stdin und stdout (und vielleicht stderr wo ich gerade dabei bin) Dateihandles, damit zukünftige Aufrufe an printf() oder putchar() oder puts() wird in eine Datei gehen, und zukünftige Aufrufe an getc() und solche werden aus einer Datei kommen.

1) Ich möchte die Standardeingabe/-ausgabe/-fehler nicht dauerhaft verlieren. Vielleicht möchte ich sie später im Programm wiederverwenden.

2) Ich möchte keine neuen Dateihandles öffnen, weil diese Dateihandles entweder viel herumgereicht werden müssten oder global (schaudern).

3) Ich möchte keine verwenden open() oder fork() oder andere systemabhängige Funktionen, wenn ich nicht anders kann.

Also im Grunde funktioniert es, dies zu tun:

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

Und wenn ja, wie bekomme ich den ursprünglichen Wert von stdin der Rücken? Muss ich es in einem speichern FILE * und später wieder zurück?

  • Beachten Sie, dass stdin, stdoutund stderr sind globale Variablen.

    – Alexis Wilke

    22. Januar 2018 um 6:59 Uhr

Benutzeravatar von bk1e
bk1e

Warum verwenden freopen()? Die C89-Spezifikation enthält die Antwort in einer der Endnoten für den Abschnitt über <stdio.h>:

116. Die primäre Verwendung der freopen Die Funktion besteht darin, die mit einem Standardtextstream verknüpfte Datei zu ändern (stderr,
stdinoder stdout), da diese Bezeichner keine änderbaren Lvalues ​​sein müssen, denen der von der zurückgegebene Wert entspricht fopen Funktion zugeordnet werden kann.

freopen wird häufig missbraucht, z stdin = freopen("newin", "r", stdin);. Dies ist nicht mehr tragbar als fclose(stdin); stdin = fopen("newin", "r");. Beide Ausdrücke versuchen zuzuordnen stdinderen Zuweisung nicht garantiert ist.

Die richtige Art zu verwenden freopen ist die Zuordnung wegzulassen: freopen("newin", "r", stdin);

  • @AbiusX Ich bin mir nicht sicher, wie portabel das ist, aber unter POSIX-Umgebungen können Sie dies auf andere Weise tun – indem Sie die Bedeutung des zugrunde liegenden Handles mit ändern dup2(): linux.die.net/man/3/fflush. Auf diese Weise können Sie das STDOUT-Handle duplizieren, bevor Sie es ersetzen, und dann zurückkopieren. Denken Sie daran fflush(). Hier ist ein Beispiel dafür, wie dies zum Forken verwendet wird: stackoverflow.com/questions/9405985/…

    – Philip Couling

    7. Oktober 2014 um 9:40 Uhr

  • Für diejenigen, die den Standarddeskriptor schließen möchten, bevor er ihn erneut öffnet, gibt es eine in msvc2015 UCRT getestete Lösung, use sequence of 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 */. Das hat Sinn, weil freopen verwendet einen bereits zugewiesenen Deskriptor nicht wieder und weist einen neuen zu. Um es also zu zwingen, den Deskriptor wiederzuverwenden, müssen Sie es zuerst schließen.

    – Andry

    1. August 2021 um 9:10 Uhr


Benutzeravatar von John T
Johannes T

Ich denke du suchst sowas wie freopen()

  • Gibt es einen Unterschied zwischen stdin = freopen(“newin”, “r”, stdin); und fclose(stdin); stdin = fopen(“newin”, “r”); oder Nein?

    – Chris Lutz

    25. Februar 2009 um 6:02 Uhr

  • fopen öffnet ein Dateiobjekt, während freopen Ihren Datenstrom auf das Dateiobjekt zeigt. Von der Seite „Diese Funktion ist besonders nützlich, um vordefinierte Streams wie stdin, stdout und stderr auf bestimmte Dateien umzuleiten“

    – Johannes T

    25. Februar 2009 um 6:11 Uhr

  • Gibt es eine Möglichkeit, stdin/stdout mit ihren ursprünglichen Werten außer stdin = freopen(“/dev/tty”, “r”); oder so?

    – Chris Lutz

    25. Februar 2009 um 6:25 Uhr

  • Sie verwenden fclose mit dem Stream, auf den Sie auf die Datei verwiesen haben. fclose(stdout);

    – Johannes T

    25. Februar 2009 um 6:32 Uhr

  • Klarstellung: Syntaktische Zuweisung an stdinwie in stdin = fopen(...) oder stdin = freopen(...)ist nicht portabel, weil stdin darf ein Makro sein. stdio.h könnte enthalten #define stdin __builtin_get_stdin(), und natürlich können Sie das nicht auf die linke Seite eines Auftrags schreiben. @ChrisLutz, die richtige Art zu verwenden freopen ist einfach if (freopen("newin", "r", stdin) != NULL) ... — Der Rückgabewert ist nützlich, um anzuzeigen, ob das Öffnen fehlgeschlagen ist, aber Sie sollten ihn im Allgemeinen nichts zuweisen müssen.

    – Quuxplusone

    19. Dezember 2013 um 18:06 Uhr

Dies ist eine modifizierte Version der Methode von Tim Post; Ich habe /dev/tty anstelle von /dev/stdout verwendet. Ich weiß nicht, warum es nicht mit stdout funktioniert (was ein Link zu /proc/self/fd/1 ist):

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

Durch die Verwendung von /dev/tty wird die Ausgabe auf das Terminal umgeleitet, von dem aus die App gestartet wurde.

Hoffe, diese Informationen sind nützlich.

Benutzeravatar von Tim Post
Tim Post

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

... do your stuff

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

Das spitzt die Nadel auf meinem Rund-Stift-Quadrat-Loch-O-Meter, was versuchst du zu erreichen?

Bearbeiten:

Denken Sie daran, dass stdin, stdout und stderr die Dateideskriptoren 0, 1 und 2 für jeden neu erstellten Prozess sind. freopen() sollte die gleichen fds behalten, weise ihnen einfach neue Streams zu.

Ein guter Weg, um sicherzustellen, dass dies tatsächlich das tut, was Sie wollen, wäre:

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

Ich glaube, dass dies das erwartete Verhalten von freopen() ist, wie Sie sehen können, verwenden Sie immer noch nur drei Dateideskriptoren (und zugehörige Streams).

Dies würde jede Shell-Umleitung außer Kraft setzen, da es für die Shell nichts zum Umleiten gäbe. Allerdings wird es wahrscheinlich Rohre brechen. Vielleicht möchten Sie sichergehen, dass Sie einen Handler für SIGPIPE einrichten, falls sich Ihr Programm am blockierenden Ende einer Pipe (nicht FIFO, Pipe) befindet.

Daher sollte ./your_program –stdout /tmp/stdout.txt –stderr /tmp/stderr.txt einfach mit freopen() und unter Beibehaltung der gleichen tatsächlichen Dateideskriptoren ausgeführt werden. Was ich nicht verstehe, ist, warum Sie sie zurücksetzen müssen, nachdem Sie sie geändert haben? Wenn jemand eine der beiden Optionen bestanden hat, möchte er sicherlich, dass sie bestehen bleibt, bis das Programm beendet wird?

Benutzeravatar von gahooa
gahooa

Die os-Funktion dup2() sollte bieten, was Sie brauchen (wenn nicht Verweise auf genau das, was Sie brauchen).

Genauer gesagt, Sie können den stdin-Dateideskriptor mit dup2() in einen anderen Dateideskriptor kopieren, andere Dinge mit stdin tun und ihn dann zurückkopieren, wenn Sie möchten.

Die Funktion dup() dupliziert einen offenen Dateideskriptor. Insbesondere stellt sie eine alternative Schnittstelle zu dem Dienst bereit, der von der Funktion fcntl() bereitgestellt wird, indem sie den konstanten Befehlswert F_DUPFD mit 0 als drittem Argument verwendet. Der duplizierte Dateideskriptor teilt alle Sperren mit dem Original.

Bei Erfolg gibt dup() einen neuen Dateideskriptor zurück, der Folgendes mit dem Original gemeinsam hat:

  • Gleiche geöffnete Datei (oder Pipe)
  • Gleicher Dateizeiger (beide Dateideskriptoren teilen sich einen Dateizeiger)
  • Gleicher Zugriffsmodus (Lesen, Schreiben oder Lesen/Schreiben)

  • Die Frage besagt jedoch ausdrücklich, dass keine systemabhängigen Funktionen verwendet werden sollen.

    – abschalten

    25. Februar 2009 um 6:55 Uhr

  • Und die Frage besagt ausdrücklich, dass die Anzahl der offenen Streams / Dateideskriptoren begrenzt wird

    – Tim Post

    25. Februar 2009 um 15:23 Uhr

Benutzeravatar von Norman Ramsey
Norman Ramsey

freopen löst den einfachen Teil. Es ist nicht schwer, die alte Standardeinstellung beizubehalten, wenn Sie nichts gelesen haben und bereit sind, POSIX-Systemaufrufe wie zu verwenden dup oder dup2. Wenn Sie anfangen, daraus zu lesen, sind alle Wetten abgeschlossen.

Vielleicht können Sie uns sagen, in welchem ​​Kontext dieses Problem auftritt?

Ich möchte Sie ermutigen, sich an Situationen zu halten, in denen Sie bereit sind, Altes aufzugeben stdin und stdout und somit verwenden können freopen.

  • Die Frage besagt jedoch ausdrücklich, dass keine systemabhängigen Funktionen verwendet werden sollen.

    – abschalten

    25. Februar 2009 um 6:55 Uhr

  • Und die Frage besagt ausdrücklich, dass die Anzahl der offenen Streams / Dateideskriptoren begrenzt wird

    – Tim Post

    25. Februar 2009 um 15:23 Uhr

johns Benutzeravatar
John

Und in der Zwischenzeit gibt es eine C-Quellcodebibliothek, die all dies für Sie erledigt und stdout oder stderr umleitet. Aber der coole Teil ist, dass Sie den abgefangenen Streams beliebig viele Callback-Funktionen zuweisen können, sodass Sie dann ganz einfach eine einzelne Nachricht an mehrere Ziele, eine DB, eine Textdatei usw. senden können.

Darüber hinaus ist es trivial, neue Streams zu erstellen, die genauso aussehen und sich genauso verhalten wie stdout und stderr, wobei Sie diese neuen Streams auch an mehrere Speicherorte umleiten können.

Suchen Sie auf *oogle nach der U-Streams C-Bibliothek.

1417630cookie-checkUmleitung von stdin und stdout von C

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

Privacy policy