So leiten Sie die Ausgabe nach freopen(“out.txt”, “a”, stdout) wieder auf den Bildschirm um

Lesezeit: 8 Minuten
#include <stdio.h>

int main() {
    printf("This goes to screen\n");
    freopen("out.txt", "a", stdout);
    printf("This goes to out.txt");
    freopen("/dev/stdout", "a", stdout);
    printf("This should go to screen too, but doesn't\n");

    return 0;
}

Ich rufe freopen an, um die umzuleiten stdout in out.txt drucke ich dann etwas auf die Datei, jetzt möchte ich es wieder auf den Bildschirm umleiten, aber freopen(“/dev/stdout”, “a”, stdout); funktioniert nicht. Gibt es eine Möglichkeit, dies mit ANSI C- oder POSIX-Systemaufrufen zu tun?

Ich kann mir keine Möglichkeit vorstellen, dies plattformübergreifend zu tun, aber auf GNU/Linux-Systemen (und vielleicht auch anderen POSIX-kompatiblen) ist dies möglich freopen ("/dev/tty", "a", stdout). Ist es das, was Sie versucht haben?

  • Dies wird die Dinge im Falle einer Umleitung oder Weiterleitung an einen anderen Prozess beschädigen.

    – Michael Krelin – Hacker

    15. Dezember 2009 um 19:07 Uhr

  • Nur der letzte Prozess, den ich aufforke (), muss auf den Bildschirm umgeleitet werden. Das funktioniert also in meinem Fall.

    – Hoffmann

    15. Dezember 2009 um 20:30 Uhr

  • Hoffman, natürlich kann es für Ihren speziellen Fall gut genug sein, also schreibe ich diese Kommentare für diejenigen, die die Frage finden und sich vielleicht um den Fall kümmern, wenn das Ganze umgeleitet wird oder überhaupt kein tty hat.

    – Michael Krelin – Hacker

    15. Dezember 2009 um 23:01 Uhr

  • Ich stimme zu, Hacker. Ich mag die Lösung von Jonathan Leffler unten, in der wir einen Wrapper für printf und einen Standardstream haben. Eine sehr schöne Lösung und auch plattformübergreifend (soweit ich das beurteilen kann).

    – Patrick Niedzielski

    17. Dezember 2009 um 20:57 Uhr

Leider scheint es keinen guten Weg zu geben:

http://c-faq.com/stdio/undofreopen.html

Die beste Empfehlung ist, freopen unter diesen Umständen nicht zu verwenden.

Benutzer-Avatar
Michael Krelin – Hacker

Generell kann man das nicht. Sie haben die Datei geschlossen, die eine Pfeife oder was auch immer sein könnte. Es kann nicht wieder geöffnet werden. Vielleicht hast du gespart stdout Wert, weisen Sie ihm dann etwas fopen zu und schließen Sie es dann und kopieren Sie den alten Wert zurück. Beispiel:

FILE *o = stdout;
stdout=fopen("/tmp/crap.txt","a");
printf("Oh no!\n");
fclose(stdout);
stdout = o;

Mike Weller schlug unten in Kommentaren vor, dass stdout möglicherweise nicht immer beschreibbar ist. In diesem Fall könnte so etwas helfen:

int o = dup(fileno(stdout));
freopen("/tmp/crap.txt","a",stdout);
printf("Oh no!\n");
dup2(o,fileno(stdout));
close(o);

Eine weitere Bearbeitung: Wenn Sie damit die Ausgabe des untergeordneten Prozesses umleiten, wie Ihr Kommentar an anderer Stelle vorschlägt, können Sie ihn nach der Verzweigung umleiten.

  • Außer dass stdio unter einigen Implementierungen wahrscheinlich nicht zuverlässig beschreibbar ist.

    – Mike Weller

    15. Dezember 2009 um 17:50 Uhr

  • Mike Weller, könnte sein. Ich habe versucht, eine alternative Lösung anzubieten.

    – Michael Krelin – Hacker

    15. Dezember 2009 um 19:06 Uhr

Benutzer-Avatar
Jonathan Leffler

Verwenden fdopen() und dup() ebenso gut wie freopen().

int old_stdout = dup(1);  // Preserve original file descriptor for stdout.

FILE *fp1 = freopen("out.txt", "w", stdout);  // Open new stdout

...write to stdout...   // Use new stdout

FILE *fp2 = fdopen(old_stdout, "w");   // Open old stdout as a stream

...Now, how to get stdout to refer to fp2?
...Under glibc, I believe you can use:

fclose(stdout);    // Equivalent to fclose(fp1);
stdout = fp2;      // Assign fp2 to stdout
// *stdout = *fp2;   // Works on Solaris and MacOS X, might work elsewhere.

close(old_stdout);   // Close the file descriptor so pipes work sanely

Ich bin mir nicht sicher, ob Sie die Aufgabe woanders zuverlässig erledigen können.

Dubioser Code, der tatsächlich funktioniert

Der folgende Code funktionierte unter Solaris 10 und MacOS X 10.6.2 – aber ich bin nicht sicher, ob er zuverlässig ist. Die Strukturzuweisung kann mit Linux glibc funktionieren oder nicht.

#include <stdio.h>
#include <unistd.h>

int main(void)
{
    printf("This goes to screen\n");
    int old_stdout = dup(1);  // Consider dup(STDOUT_FILENO) or dup(fileno(stdout))
    FILE *fp1 = freopen("out.txt", "a", stdout);
    printf("This goes to out.txt\n");
    fclose(stdout);
    FILE *fp2 = fdopen(old_stdout, "w");
    *stdout = *fp2;                       // Unreliable!
    printf("This should go to screen too, but doesn't\n");

    return 0;
}

Du kannst nicht sagen, dass du nicht gewarnt wurdest – das ist ein Spiel mit dem Feuer!

Wenn Sie sich auf einem System mit dem befinden /dev/fd Dateisystem, könnten Sie den Namen der Datei erstellen, der durch den zurückgegebenen Dateideskriptor impliziert wird dup() mit sprintf(buffer, "/dev/fd/%d", old_stdout) und dann verwenden freopen() mit diesem Namen. Dies wäre viel zuverlässiger als die in diesem Code verwendete Zuweisung.

Die besseren Lösungen lassen den Code entweder überall ‘fprintf(fp, …)’ verwenden oder verwenden eine Cover-Funktion, mit der Sie Ihren eigenen Standarddateizeiger festlegen können:

mprintf.c

#include "mprintf.h"
#include <stdarg.h>

static FILE *default_fp = 0;

void set_default_stream(FILE *fp)
{
    default_fp = fp;
}

int mprintf(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);

    if (default_fp == 0)
        default_fp = stdout;

    int rv = vfprintf(default_fp, fmt, args);

    va_end(args);
    return(rv);
 }

mprintf.h

#ifndef MPRINTF_H_INCLUDED
#define MPRINTF_H_INCLUDED

#include <stdio.h>

extern void set_default_stream(FILE *fp);
extern int  mprintf(const char *fmt, ...);

#endif

Natürlich können Sie nach Bedarf ein mvprintf() und andere Funktionen erstellen.

Beispielverwendung von mprintf()

Dann können Sie anstelle des ursprünglichen Codes Folgendes verwenden:

#include "mprintf.h"

int main()
{
    mprintf("This goes to screen\n");
    FILE *fp1 = fopen("out.txt", "w");
    set_default_stream(fp1);
    mprintf("This goes to out.txt\n");
    fclose(fp1);
    set_default_stream(stdout);
    mprintf("This should go to screen too, but doesn't\n");

    return 0;
}

(Warnung: ungetesteter Code – Konfidenzniveau zu hoch. Außerdem wird der gesamte Code unter der Annahme geschrieben, dass Sie einen C99-Compiler verwenden, hauptsächlich weil ich Variablen deklariere, wenn ich sie zum ersten Mal benötige, nicht am Anfang der Funktion.)


Vorsicht:

Beachten Sie, dass wenn das ursprüngliche Programm aufgerufen wird als ./original_program > file oder ./original_program | grep something (mit umgeleiteter Ausgabe) oder wird von a ausgeführt cron Job, dann Eröffnung /dev/tty ist normalerweise nicht geeignet, um die Standardausgabe erneut zu öffnen, da die ursprüngliche Standardausgabe nicht das Terminal war.

Beachten Sie auch, dass die Reihenfolge der Operationen falsch ist, wenn die Umleitung der Standardausgabe vor dem Forken und Ausführen eines untergeordneten Programms verwendet wird und die ursprüngliche Standardausgabe im übergeordneten Programm wiederhergestellt wird. Sie sollten (nur) die E / A des untergeordneten Elements forken und dann anpassen, ohne die E / A des übergeordneten Elements überhaupt zu ändern.

Unter Windows können Sie “CONOUT$” öffnen.

freopen("test.txt", "w", stdout);
printf("this goes to test.txt");
freopen("CONOUT$", "w", stdout);
printf("this goes to the console\n");

Dies funktioniert wahrscheinlich nicht, wenn stdout zu Beginn umgeleitet wird.

  • Ihre Lösung erlaubte mir nicht, stderr wiederherzustellen. Dieser Artikel über _dup funktioniert bei mir.

    – Brandlingo

    22. Oktober 2014 um 15:04 Uhr


  • @cegprakash Hast du CONIN$ für stdin geöffnet?

    – Benutzer253751

    29. März 2016 um 19:38 Uhr

  • Ich verwende nur freopen (“test1.txt”, “r”, stdin); und nach einiger Zeit möchte ich freopen (“test2.txt”, “r”, stdin) ausführen, aber stdin zeigt immer noch auf die erste Datei.

    – cegprakash

    1. April 2016 um 10:05 Uhr


Benutzer-Avatar
JackCColeman

Der folgende Code (SwapIOB) wird in Testbenches verwendet, die den stdout-Stream zum Vergleich mit einer erwarteten Ergebnisdatei speichern möchten.

Hintergrund: Dateistreams werden mit einer _IOB-Struktur verwaltet, die in einem Array von 20 _IOB-Einträgen gespeichert wird. Dies schließt den stdout-Stream ein. Die IOBs werden in einem Array gespeichert. Wenn eine Datei erstellt wird, erhält der Anwendungscode einen PTR zu einem Element in diesem Array. Der Anwendungscode übergibt diesen ptr dann an das Betriebssystem zur Verarbeitung von E/A-Aufrufen. Somit enthält oder stützt sich das OS selbst NICHT auf seine eigenen Zeiger auf den IOB der Anwendung.

Anforderung: Beim Ausführen einer Testbench sollen die von einer Anwendung ausgegebenen stdout-Meldungen in eine Datei umgeleitet werden. Nachdem das zu testende Modul jedoch abgeschlossen ist, sollten stdout-Meldungen an die Konsole umgeleitet werden.

Diese Routine wurde getestet und wird derzeit auf Windows XP/Pro-Systemen verwendet.

void SwapIOB(FILE *A, FILE *B) {

    FILE temp;

    // make a copy of IOB A (usually this is "stdout")
    memcpy(&temp, A, sizeof(struct _iobuf));

    // copy IOB B to A's location, now any output
    // sent to A is redirected thru B's IOB.
    memcpy(A, B, sizeof(struct _iobuf));

    // copy A into B, the swap is complete
    memcpy(B, &temp, sizeof(struct _iobuf));

}  // end SwapIOB;

Anwendungscode verwendet SwapIOB() ähnlich wie:

FILE *fp;

fp = fopen("X", "w");

SwapIOB(stdout, fp);

printf("text to file X");

SwapIOB(stdout, fp);

fclose(fp);

printf("text to console works, again!");

  • Ihre Lösung erlaubte mir nicht, stderr wiederherzustellen. Dieser Artikel über _dup funktioniert bei mir.

    – Brandlingo

    22. Oktober 2014 um 15:04 Uhr


  • @cegprakash Hast du CONIN$ für stdin geöffnet?

    – Benutzer253751

    29. März 2016 um 19:38 Uhr

  • Ich verwende nur freopen (“test1.txt”, “r”, stdin); und nach einiger Zeit möchte ich freopen (“test2.txt”, “r”, stdin) ausführen, aber stdin zeigt immer noch auf die erste Datei.

    – cegprakash

    1. April 2016 um 10:05 Uhr


1381540cookie-checkSo leiten Sie die Ausgabe nach freopen(“out.txt”, “a”, stdout) wieder auf den Bildschirm um

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

Privacy policy