Dlaczego „>” nie przekierowuje komunikatów o błędach z gcc?

9

Zapisałem następujący program w new.c

int main() 
{ 
    a;
    return 0; 
}

Zwraca komunikat o błędzie. Chcę wysłać tę wiadomość do pliku. Więc użyłem następującego polecenia

gcc new.c > temp.txt

Ale wciąż otrzymywałem dane wyjściowe na terminalu. Używam Ubuntu 13.04. Jak mogę to zrobić?

Alex
źródło
W celach informacyjnych zobacz także Jakie są operatory kontroli i przekierowania powłoki?
G-Man mówi „Przywróć Monikę”

Odpowiedzi:

16

Kiedy kompilujesz program gcc, istnieją różne rodzaje danych wyjściowych: do stdouti stderr. Zwykle >przekieruje stdoutstrumień do pliku (na przykład wynik a printf("hello world\n");zostanie wysłany do stdout). Jednak stderrnadal jest wysyłany na ekran, ponieważ zakłada się, że jest to „coś wyjątkowego, o czym trzeba powiedzieć”.

Istnieje sposób przekierowania stderr do pliku - robisz to za pomocą następującego (niezbyt intuicyjnego) polecenia:

gcc new.c &> myFile

gdzie &>jest „bash shorthand” dla „przekieruj wszystko”. Jak zauważył @CharlesDuffy, forma zgodna z POSIX to

gcc new.c > myFile 2>&1

Oznacza to „skompiluj 'new.c' i wyślij stdoutdo myFile. I wyślij stderr(2) w to samo miejsce co stdout( &1=" to samo miejsce co standardowe wyjście ").

Więcej informacji na temat różnych przekierowań znajdziesz na stronie http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO-3.html i http://mywiki.wooledge.org/BashFAQ/055

Nawiasem mówiąc, jeśli chcesz wysłać coś bezpośrednio z programu stderr, możesz to zrobić w następujący sposób

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

Jeśli umieścisz to w programie, uruchom program i wyślij „normalne” wyjście do pliku, nadal będzie ono widoczne na konsoli. Jeśli więc skompilujesz powyższe w pliku wykonywalnym urgent, wpisz

./urgent > /dev/null

na konsoli twoje wyjście pojawi się na ekranie.

Floris
źródło
2
mywiki.wooledge.org/BashFAQ/055 jest prawdopodobnie lepszym wprowadzeniem do przekierowania. Ponadto naprawdę należy wprowadzić formularz ( >myFile 2>&1) zgodny z POSIX, a także rozszerzenie bash ( &>).
Charles Duffy
@CharlesDuffy - oba bardzo dobre punkty. Uwzględnię je w mojej odpowiedzi na kompletność.
Floris
11

Ponieważ >przekierowuje tylko standardowe wyjście i zapisywane są błędy stderr, należy zamiast tego użyć jednego z poniższych:

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

...lub...

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

&>jest rozszerzeniem BASH, które przekierowuje zarówno plik, jak stdouti stderrplik; inaczej, najprostszym rozwiązaniem jest pierwszym stdout przekierowania ( >temp.txt), a następnie dokonać stderr (FD 2) kopię już zostało przekształcone uchwytu pliku na standardowe wyjście (FD 1), tak: 2>&1.

nims
źródło
4

Jak powiedzieli inni, linux zapewnia dwa różne strumienie wyjściowe:

standardowe wyjście , czyli „standardowe wyjście”, jest tym, do czego trafiają wszystkie zwykłe wyjścia.
              Możesz odwoływać się do niego za pomocą deskryptora pliku 1.

stderr lub „błąd standardowy” to osobny strumień dla informacji poza pasmem.
              Możesz odwoływać się do niego za pomocą deskryptora pliku 2.

Dlaczego dwa różne strumienie wyjściowe? Rozważ szereg wymyślonych poleceń:

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

Teraz wyobraź sobie, że decryptpolecenie kończy się niepowodzeniem i generuje komunikat o błędzie. Gdyby wysłał tę wiadomość stdout, wysłałby ją do potoku, a gdyby nie miał słowa „sekret”, nigdy by go nie zobaczył. W rezultacie otrzymałeś pusty plik wyjściowy, nie mając pojęcia, co poszło nie tak.

Ponieważ jednak rura tylko przechwytuje stdout, decryptpolecenie może wysłać swoje błędy do stderr, gdzie zostaną wyświetlone na konsoli.

Możesz przekierować stdouti stderrrazem lub osobno:

# 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

Możesz przekierować błędy stdouti przetworzyć je tak, jakby były normalnym wyjściem:

# 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

Można również użyć „skrótowej” notacji przekierować zarówno stdout i stderr do tego samego pliku:

decrypt $MY_FILE &> output.txt

I na koniec >operator najpierw obetnie swój plik wyjściowy przed zapisaniem do niego. Jeśli zamiast tego chcesz dołączyć dane do istniejącego pliku, użyj >>operatora:

decrypt $MY_FILE 2>> more_errors.txt >> more_secrets.txt
decrypt $MY_FILE >> more_output.txt 2>&1
Adam Liss
źródło
1
Dwa spory: (1) Użycie niecytowanych rozszerzeń parametrów ( $FOO) jest częstym źródłem błędów, a pokazanie tego w przykładach nie jest takie świetne. (2) Używanie nazw zmiennych pisanych wielkimi literami jest głównym powodem konfliktów przestrzeni nazw między zmiennymi środowiskowymi a zmiennymi wbudowanymi (wielkie litery według konwencji) i zmiennymi lokalnymi (małe litery według konwencji). (3) Zachęcanie ludzi do wielokrotnego używania >>(co powoduje ponowne otwieranie pliku za każdym razem, gdy jest on używany w poleceniu) zamiast otwierania pliku raz i pozostawianie deskryptora pliku otwartego do użycia przez wiele poleceń powoduje nieefektywny kod.
Charles Duffy
... w ostatnim punkcie, porównaj z: exec 4>secrets; echo "this is a secret" >&4; echo "this is another secret" >&4
Charles Duffy
+1 Dzięki za utrzymanie mnie w uczciwości @CharlesDuffy. Wszystkie dobre punkty. Celowo zrezygnowałem execz uproszczenia, chociaż w praktyce jest to ogólnie lepsza strategia.
Ponadto, mogą być zwijane do lub (tam, gdzie muszą być odstępy przed i po i przed ). command₁ > output_file ; command₂ >> the_same_output_file( command₁ ; command₂ )  > output_file{ command₁ ; command₂ ;  }  > output_file{;}
G-Man mówi „Przywróć Monikę”