Por que não '>' redireciona mensagens de erro do gcc?

9

Eu armazenei o seguinte programa em new.c

int main() 
{ 
    a;
    return 0; 
}

Ele retorna uma mensagem de erro. Eu quero enviar esta mensagem para um arquivo. Então eu usei o seguinte comando

gcc new.c > temp.txt

Mas ainda assim eu estava recebendo a saída no terminal. Eu estou usando o Ubuntu 13.04. Como posso fazer isso funcionar?

Alex
fonte

Respostas:

15

Quando você compila um programa com gcc, existem diferentes tipos de saída: para stdout e stderr. Normalmente, o > vai dirigir stdout fluxo para um arquivo (por exemplo, o resultado de um printf("hello world\n"); é enviado para stdout ). No entanto, o stderr continua a ser enviado para a tela, desde que é assumido como "algo excepcional que você precisa ser informado sobre".

Existe uma maneira de redirecionar o stderr para um arquivo - você faz isso com o seguinte comando (não muito intuitivo):

gcc new.c &> myFile

Onde &> é "shorthand" para "redirecionar tudo". Como foi apontado por @CharlesDuffy, o formato compatível com POSIX é

gcc new.c > myFile 2>&1

Isso significa "compilar 'new.c' e enviar stdout para myFile. E envia stderr (2) para o mesmo lugar que stdout ( &1 = "o mesmo lugar que stdout").

Você encontrará mais detalhes sobre diferentes redirecionamentos em http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO-3.html e http://mywiki.wooledge.org/BashFAQ/055

By the way, se você quiser enviar algo de dentro de seu programa especificamente para stderr, você pode fazer isso com o seguinte

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

Se você incluir isso em um programa, executar o programa e enviar a saída "normal" para um arquivo, isso ainda aparecerá no console. Então, se você compilar o acima no executável urgente depois digite

./urgent > /dev/null

no console, sua saída aparecerá na tela.

Floris
fonte
2
mywiki.wooledge.org/BashFAQ/055 é provavelmente uma introdução melhor ao redirecionamento. Além disso, deve-se realmente introduzir o formulário compatível com POSIX ( >myFile 2>&1 ) bem como a extensão do bash ( &> ).
Charles Duffy
@CharlesDuffy - ambos muito bons pontos. Vou incluí-los na minha resposta para a integralidade.
Floris
11

Porque > redireciona apenas stdout e erros são gravados em stderr, você precisa usar um dos seguintes:

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

...ou...

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

&> é uma extensão BASH que redireciona ambos stdout e stderr para um arquivo; caso contrário, a abordagem mais fácil é primeiro redirecionar o stdout ( >temp.txt ), e então faça stderr (FD 2) uma cópia do identificador de arquivo já redirecionado no stdout (FD 1), da seguinte forma: 2>&1.

nims
fonte
4

Como os outros disseram, o linux fornece dois fluxos de saída diferentes:

stdout , ou "saída padrão" é onde vai toda a saída regular.
Você pode referenciá-lo usando o descritor de arquivo 1.

stderr ou "erro padrão" é um fluxo separado para informações fora de banda.
Você pode referenciá-lo usando o descritor de arquivo 2.

Por que dois fluxos de saída diferentes? Considere um pipeline de comandos imaginários:

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

Agora imagine o decrypt comando falha e gera uma mensagem de erro. Se ele enviou essa mensagem para stdout, ele enviaria para o tubo e, a menos que tivesse a palavra "secreto", você nunca o veria. Então você acabaria com um arquivo de saída vazio, sem ideia do que deu errado.

No entanto, como o tubo captura apenas stdout, a decrypt comando pode enviar seus erros para stderr, onde eles serão exibidos no console.

Você pode redirecionar stdout e stderr, juntos ou independentemente:

# 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

Você pode redirecionar os erros para stdout e processá-los como se fossem saída normal:

# 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

Você também pode usar uma notação "abreviada" para redirecionar ambos stdout e stderr para o mesmo arquivo:

decrypt $MY_FILE &> output.txt

E, finalmente, o > operador primeiro truncar seu arquivo de saída antes de gravar nele. Se, em vez disso, você quiser acrescentar dados para um arquivo existente, use o >> operador:

decrypt $MY_FILE 2>> more_errors.txt >> more_secrets.txt
decrypt $MY_FILE >> more_output.txt 2>&1
Adam Liss
fonte
1
Dois argumentos: (1) Uso de expansões de parâmetros sem aspas ( $FOO ) é uma fonte comum de erros, e demonstrá-lo nos exemplos não é tão bom. (2) O uso de nomes de variáveis ​​maiúsculas é o principal motivo para conflitos de espaço de nomes entre ambiente e variáveis ​​internas (maiúsculas por convenção) e variáveis ​​locais (minúsculas por convenção). (3) Encorajar as pessoas a usar repetidamente >> (que reabre o arquivo toda vez que é usado em um comando) ao invés de abrir um arquivo uma vez e deixando o descritor de arquivos aberto para uso por vários comandos resulta em código ineficiente.
Charles Duffy
... no último ponto, compare com: exec 4>secrets; echo "this is a secret" >&4; echo "this is another secret" >&4
Charles Duffy
+1 Obrigado por me manter honesto, @CharlesDuffy. Todos bons pontos. Intencionalmente omitido exec por simplicidade, embora na prática seja geralmente uma estratégia melhor.
Além disso, command₁ > output_file ; command₂ >> the_same_output_file pode ser recolhido para ( command₁ ; command₂ )  > output_file ou { command₁ ; command₂ ;  }  > output_file (onde deve haver espaços antes e depois do {e um ; antes de o } ).
G-Man