Warum leitet '>' keine Fehlermeldungen von gcc um?

9

Ich habe das folgende Programm in new.c gespeichert

int main() 
{ 
    a;
    return 0; 
}

Es wird eine Fehlermeldung zurückgegeben. Ich möchte diese Nachricht an eine Datei senden. Also habe ich den folgenden Befehl verwendet

gcc new.c > temp.txt

Trotzdem bekam ich die Ausgabe auf dem Terminal. Ich benutze Ubuntu 13.04. Wie kann ich es zum Laufen bringen?

Alex
quelle
Weitere Informationen finden Sie unter Was sind die Steuerungs- und Umleitungsoperatoren der Shell?
G-Man sagt 'Reinstate Monica'

Antworten:

16

Wenn Sie ein Programm mit kompilieren gcc, gibt es verschiedene Arten der Ausgabe: an stdoutund stderr. Normalerweise >leitet der stdoutStream den Stream in eine Datei (z. B. wird das Ergebnis von a an printf("hello world\n");gesendet stdout). Das wird jedoch stderrweiterhin an den Bildschirm gesendet, da davon ausgegangen wird, dass es sich um "etwas Außergewöhnliches handelt, über das Sie informiert werden müssen".

Es gibt eine Möglichkeit, stderr in eine Datei umzuleiten - Sie tun dies mit dem folgenden (nicht sehr intuitiven) Befehl:

gcc new.c &> myFile

Wo &>ist "Bash Shorthand" für "Alles umleiten". Wie von @CharlesDuffy hervorgehoben wurde, ist das POSIX-kompatible Formular

gcc new.c > myFile 2>&1

Dies bedeutet "kompiliere 'new.c' und sende stdoutan myFile. Und sende stderr(2) an den gleichen Ort wie stdout( &1=" den gleichen Ort wie stdout ").

Weitere Informationen zu verschiedenen Weiterleitungen finden Sie unter http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO-3.html und http://mywiki.wooledge.org/BashFAQ/055

Übrigens, wenn Sie etwas aus Ihrem Programm heraus speziell an senden möchten stderr, können Sie dies wie folgt tun

fprintf(stderr, "hello world - this is urgent.\n");

Wenn Sie dies in ein Programm aufnehmen, das Programm ausführen und die "normale" Ausgabe an eine Datei senden, wird dies weiterhin auf der Konsole angezeigt. Wenn Sie also die oben in die ausführbare Datei kompilieren urgent, geben Sie dann

./urgent > /dev/null

An der Konsole wird Ihre Ausgabe auf dem Bildschirm angezeigt.

Floris
quelle
2
mywiki.wooledge.org/BashFAQ/055 ist wahrscheinlich eine bessere Einführung in die Umleitung. Außerdem sollte man unbedingt die POSIX-kompatible Form ( >myFile 2>&1) sowie die Bash-Erweiterung ( &>) einführen .
Charles Duffy
@ CharlesDuffy - beide sehr gute Punkte. Ich werde sie der Vollständigkeit halber in meine Antwort aufnehmen.
Floris
11

Da >nur stdout umgeleitet wird und Fehler geschrieben werden, stderrmüssen Sie stattdessen eine der folgenden Methoden verwenden:

gcc new.c &> temp.txt ## redirect both stdout and stderr using bash or zsh only

...oder...

gcc new.c >temp.txt 2>&1 ## redirect both stdout and stderr in any POSIX shell

&>ist eine BASH-Erweiterung, die sowohl stdoutals auch stderrzu einer Datei umleitet ; Andernfalls ist es am einfachsten, zuerst stdout ( >temp.txt) umzuleiten und dann stderr (FD 2) zu einer Kopie des bereits umgeleiteten Dateihandles auf stdout (FD 1) zu machen 2>&1.

Nims
quelle
4

Wie die anderen gesagt haben, bietet Linux zwei verschiedene Ausgabestreams:

Bei stdout oder "Standardausgabe" werden alle regulären Ausgaben ausgeführt.
              Sie können mit dem Dateideskriptor darauf verweisen 1.

stderr oder "Standardfehler" ist ein separater Stream für Out-of-Band-Informationen.
              Sie können mit dem Dateideskriptor darauf verweisen 2.

Warum zwei verschiedene Ausgabestreams? Betrachten Sie eine Pipeline imaginärer Befehle:

 decrypt $MY_FILE | grep "secret" | sort > secrets.txt

Stellen Sie sich nun vor, der decryptBefehl schlägt fehl und generiert eine Fehlermeldung. Wenn es diese Nachricht an stdoutsenden würde, würde es in die Pipe senden, und wenn es nicht das Wort "geheim" hätte, würden Sie es nie sehen. Sie erhalten also eine leere Ausgabedatei, ohne zu wissen, was schief gelaufen ist.

Da die Pipe jedoch nur erfasst stdout, kann der decryptBefehl seine Fehler an die Stelle senden stderr, an der sie auf der Konsole angezeigt werden.

Sie können umleiten stdoutund stderrentweder zusammen oder unabhängig voneinander:

# Send errors to "errors.txt" and output to "secrets.txt"
# The following two lines are equivalent, as ">" means "1>"
decrypt $MY_FILE 2> errors.txt > secrets.txt
decrypt $MY_FILE 2> errors.txt 1> secrets.txt

Sie können die Fehler umleiten stdoutund so verarbeiten, als wären sie normale Ausgaben:

# The operation "2>&1" means "redirect file descriptor 2 to file
# descriptor 1. So this sends all output from stderr to stdout.
# Note that the order of redirection is important.
decrypt $MY_FILE > errors.txt 2>&1 

# This may be confusing.  It will store the normal output in a file
# and send error messages to stdout, where they'll be captured by 
# the pipe and then sorted.
decrypt $MY_FILE 2>&1 > output.txt | sort

Sie können auch eine "Kurzschreibweise" verwenden, um sowohl stdout als auch stderr in dieselbe Datei umzuleiten :

decrypt $MY_FILE &> output.txt

Und schließlich der >wird Bediener zunächst gestutzt seine Ausgabedatei vor dem Schreiben. Wenn Sie stattdessen Daten an eine vorhandene Datei anhängen möchten , verwenden Sie den >>Operator:

decrypt $MY_FILE 2>> more_errors.txt >> more_secrets.txt
decrypt $MY_FILE >> more_output.txt 2>&1
Adam Liss
quelle
1
Zwei Probleme: (1) Die Verwendung von nicht zitierten Parametererweiterungen ( $FOO) ist eine häufige Fehlerquelle, und die Demonstration in Beispielen ist nicht so großartig. (2) Die Verwendung von Variablennamen in Großbuchstaben ist der Hauptgrund für Namespace-Konflikte zwischen Umgebungsvariablen und integrierten Variablen (Großbuchstaben gemäß Konvention) und lokalen Variablen (Kleinbuchstaben gemäß Konvention). (3) Das Ermutigen von Personen zur wiederholten Verwendung >>(wodurch die Datei jedes Mal neu geöffnet wird, wenn sie in einem Befehl verwendet wird), anstatt eine Datei einmal zu öffnen und den Dateideskriptor für die Verwendung durch mehrere Befehle offen zu lassen, führt zu ineffizientem Code.
Charles Duffy
... auf den letzten Punkt, zu vergleichen , zu: exec 4>secrets; echo "this is a secret" >&4; echo "this is another secret" >&4
Charles Duffy
+1 Danke, dass du mich ehrlich gehalten hast, @CharlesDuffy. Alles gute Punkte. Ich habe der execEinfachheit halber absichtlich weggelassen , obwohl es in der Praxis im Allgemeinen eine bessere Strategie ist.
Auch kann zusammengeklappt werden , um oder (wo es vor und nach der sein Leerzeichen müssen , und eine vor dem ). command₁ > output_file ; command₂ >> the_same_output_file( command₁ ; command₂ )  > output_file{ command₁ ; command₂ ;  }  > output_file{;}
G-Man sagt 'Reinstate Monica'