No shell, o que significa “2> e 1”?

Em um shell Unix, se eu quiser combinar stderr e stdout no stream stdout para manipulação adicional, posso acrescentar o seguinte no final do meu comando:

 2>&1 

Então, se eu quiser usar head na saída do g++ , posso fazer algo assim:

 g++ lots_of_errors 2>&1 | head 

então eu posso ver apenas os primeiros erros.

Eu sempre tenho dificuldade em lembrar disso, e constantemente tenho que procurar, e é principalmente porque eu não entendo completamente a syntax desse truque em particular.

Alguém pode quebrar isso e explicar personagem por personagem o que significa 2>&1 ?

O descritor de arquivo 1 é a saída padrão ( stdout ).
O descritor de arquivo 2 é o erro padrão ( stderr ).

Aqui está uma maneira de lembrar esse constructo (embora não seja totalmente preciso): no começo, 2>1 pode parecer uma boa maneira de redirect o stderr para o stdout . No entanto, ele será realmente interpretado como “redirect stderr para um arquivo chamado 1 “. & indica que o que segue é um descritor de arquivo e não um nome de arquivo. Então a construção se torna: 2>&1 .

 echo test > afile.txt 

redireciona stdout para afile.txt . Isso é o mesmo que fazer

 echo test 1> afile.txt 

Para redirect o stderr, você faz:

 echo test 2> afile.txt 

>& é a syntax para redirect um stream para outro descritor de arquivo – 0 é stdin, 1 é stdout e 2 é stderr.

Você pode redirect stdout para stderr fazendo:

 echo test 1>&2 # or echo test >&2 

Ou vice-versa:

 echo test 2>&1 

Então, resumindo … 2> redireciona stderr para um arquivo (não especificado), acrescentando &1 redireciona stderr para stdout.

Alguns truques sobre redirecionamento

Alguma particularidade de syntax sobre isso pode ter comportamentos importantes. Há algumas pequenas amostras sobre redirecionamentos, STDERR , STDOUT e ordenação de argumentos.

1 – Sobrescrevendo ou anexando?

Símbolo > redirecionamento médio.

  • > significa enviar como um arquivo completo , sobrescrevendo o alvo se existir (veja o recurso noclobber bash no # 3 depois).
  • >> mean send in addition to acrescentaria ao target se existir.

Em qualquer caso, o arquivo seria criado se não existissem.

2 – A linha de comando da shell depende da ordem!

Para testar isso, precisamos de um comando simples que envie algo em ambas as saídas :

 $ ls -ld /tmp /tnt ls: cannot access /tnt: No such file or directory drwxrwxrwt 118 root root 196608 Jan 7 11:49 /tmp $ ls -ld /tmp /tnt >/dev/null ls: cannot access /tnt: No such file or directory $ ls -ld /tmp /tnt 2>/dev/null drwxrwxrwt 118 root root 196608 Jan 7 11:49 /tmp 

(Esperando que você não tenha um diretório chamado /tnt , é claro;). Bem, nós temos isso !!

Então, vamos ver:

 $ ls -ld /tmp /tnt >/dev/null ls: cannot access /tnt: No such file or directory $ ls -ld /tmp /tnt >/dev/null 2>&1 $ ls -ld /tmp /tnt 2>&1 >/dev/null ls: cannot access /tnt: No such file or directory 

A última linha de comando despeja STDERR no console, e parece não ser o comportamento esperado … Mas …

Se você quiser fazer alguma filtragem de postagem sobre uma saída, a outra ou ambas:

 $ ls -ld /tmp /tnt | sed 's/^.*$/<-- & --->/' ls: cannot access /tnt: No such file or directory <-- drwxrwxrwt 118 root root 196608 Jan 7 12:02 /tmp ---> $ ls -ld /tmp /tnt 2>&1 | sed 's/^.*$/<-- & --->/' <-- ls: cannot access /tnt: No such file or directory ---> <-- drwxrwxrwt 118 root root 196608 Jan 7 12:02 /tmp ---> $ ls -ld /tmp /tnt >/dev/null | sed 's/^.*$/<-- & --->/' ls: cannot access /tnt: No such file or directory $ ls -ld /tmp /tnt >/dev/null 2>&1 | sed 's/^.*$/<-- & --->/' $ ls -ld /tmp /tnt 2>&1 >/dev/null | sed 's/^.*$/<-- & --->/' <-- ls: cannot access /tnt: No such file or directory ---> 

Observe que a última linha de comando neste parágrafo é exatamente igual à do parágrafo anterior, onde escrevi não parece ser o comportamento esperado (portanto, isso pode até ser um comportamento esperado).

Bem, há alguns truques sobre redirecionamentos, para fazer uma operação diferente em ambas as saídas :

 $ ( ls -ld /tmp /tnt | sed 's/^/O: /' >&9 ) 9>&2 2>&1 | sed 's/^/E: /' O: drwxrwxrwt 118 root root 196608 Jan 7 12:13 /tmp E: ls: cannot access /tnt: No such file or directory 

Nota: descritor &9 ocorreria espontaneamente devido a ) 9>&2 .

Adenda: nota! Com a nova versão do bash ( >4.0 ), há um novo recurso e uma syntax mais sexy para fazer esse tipo de coisa:

 $ ls -ld /tmp /tnt 2> >(sed 's/^/E: /') > >(sed 's/^/O: /') O: drwxrwxrwt 17 root root 28672 Nov 5 23:00 /tmp E: ls: cannot access /tnt: No such file or directory 

E, finalmente, para essa formatação de saída em cascata:

 $ ((ls -ld /tmp /tnt |sed 's/^/O: /' >&9 ) 2>&1 |sed 's/^/E: /') 9>&1| cat -n 1 O: drwxrwxrwt 118 root root 196608 Jan 7 12:29 /tmp 2 E: ls: cannot access /tnt: No such file or directory 

Adenda: nota! Mesma nova syntax, nos dois sentidos:

 $ cat -n <(ls -ld /tmp /tnt 2> >(sed 's/^/E: /') > >(sed 's/^/O: /')) 1 O: drwxrwxrwt 17 root root 28672 Nov 5 23:00 /tmp 2 E: ls: cannot access /tnt: No such file or directory 

Onde STDOUT passa por um filtro específico, STDERR para outro e finalmente ambas as saídas mescladas passam por um terceiro filtro de comando.

3 – Uma palavra sobre a opção noclobber e >| syntax

Isso é sobre sobrescrever :

Enquanto set -o noclobber instrui o bash a não sobrescrever qualquer arquivo existente, o >| syntax permitem passar por esta limitação:

 $ testfile=$(mktemp /tmp/testNoClobberDate-XXXXXX) $ date > $testfile ; cat $testfile Mon Jan 7 13:18:15 CET 2013 $ date > $testfile ; cat $testfile Mon Jan 7 13:18:19 CET 2013 $ date > $testfile ; cat $testfile Mon Jan 7 13:18:21 CET 2013 

O arquivo é sobregravado a cada vez, bem agora:

 $ set -o noclobber $ date > $testfile ; cat $testfile bash: /tmp/testNoClobberDate-WW1xi9: cannot overwrite existing file Mon Jan 7 13:18:21 CET 2013 $ date > $testfile ; cat $testfile bash: /tmp/testNoClobberDate-WW1xi9: cannot overwrite existing file Mon Jan 7 13:18:21 CET 2013 

Passe com >| :

 $ date >| $testfile ; cat $testfile Mon Jan 7 13:18:58 CET 2013 $ date >| $testfile ; cat $testfile Mon Jan 7 13:19:01 CET 2013 

Desmarcar esta opção e / ou perguntar se já está definido.

 $ set -o | grep noclobber noclobber on $ set +o noclobber $ set -o | grep noclobber noclobber off $ date > $testfile ; cat $testfile Mon Jan 7 13:24:27 CET 2013 $ rm $testfile 

4 – Último truque e mais …

Para redirect a saída de um determinado comando, vemos que uma syntax correta poderia ser:

 $ ls -ld /tmp /tnt >/dev/null 2>&1 

para este caso especial , existe uma syntax de atalho: &> … ou >&

 $ ls -ld /tmp /tnt &>/dev/null $ ls -ld /tmp /tnt >&/dev/null 

Nota: se 2>&1 existir, 1>&2 será uma syntax correta:

 $ ls -ld /tmp /tnt 2>/dev/null 1>&2 

4b- Agora, vou deixar você pensar em:

 $ ls -ld /tmp /tnt 2>&1 1>&2 | sed -es/^/++/ ++/bin/ls: cannot access /tnt: No such file or directory ++drwxrwxrwt 193 root root 196608 Feb 9 11:08 /tmp/ $ ls -ld /tmp /tnt 1>&2 2>&1 | sed -es/^/++/ /bin/ls: cannot access /tnt: No such file or directory drwxrwxrwt 193 root root 196608 Feb 9 11:08 /tmp/ 

4c- Se você estiver interessado em mais informações

Você pode ler o bom manual pressionando:

 man -Len -Pless\ +/^REDIRECTION bash 

em um console bash 😉

Os números referem-se aos descritores de arquivo (fd).

  • Zero é stdin
  • Um é stdout
  • Dois é stderr

2>&1 redireciona fd 2 para 1.

Isso funciona para qualquer número de descritores de arquivos, se o programa os usar.

Você pode olhar para /usr/include/unistd.h se você os esquecer:

 /* Standard file descriptors. */ #define STDIN_FILENO 0 /* Standard input. */ #define STDOUT_FILENO 1 /* Standard output. */ #define STDERR_FILENO 2 /* Standard error output. */ 

Dito isso, escrevi ferramentas C que usam descritores de arquivo não padrão para registro em log personalizado, para que você não o veja, a menos que você o redirecione para um arquivo ou algo assim.

Eu encontrei este post shiny sobre o redirecionamento: Tudo sobre redirecionamentos

Redirecionar a saída padrão e o erro padrão para um arquivo

$ comando &> arquivo

Este one-liner usa o operador &> para redirect ambos os streams de saída – stdout e stderr – de comando para arquivo. Este é o atalho do Bash para redirect rapidamente os dois streams para o mesmo destino.

Aqui está a aparência da tabela de descritores de arquivos depois que o Bash redirecionou os dois streams:

Digite a descrição da imagem aqui

Como você pode ver, stdout e stderr agora apontam para file . Então, qualquer coisa escrita no stdout e stderr é gravada no file .

Existem várias maneiras de redirect os dois streams para o mesmo destino. Você pode redirect cada stream um após o outro:

$ comando> arquivo 2> & 1

Essa é uma maneira muito mais comum de redirect os dois streams para um arquivo. A primeira stdout é redirecionada para o arquivo e, em seguida, stderr é duplicado para ser o mesmo que stdout. Então, ambos os streams acabam apontando para o file .

Quando o Bash vê vários redirecionamentos, ele os processa da esquerda para a direita. Vamos percorrer os passos e ver como isso acontece. Antes de executar qualquer comando, a tabela de descritores de arquivos do Bash se parece com isto:

Digite a descrição da imagem aqui

Agora o Bash processa o primeiro redirecionamento> arquivo. Já vimos isso antes e faz o stdout apontar para o arquivo:

Digite a descrição da imagem aqui

Próximo Bash vê o segundo redirecionamento 2> & 1. Nós não vimos este redirecionamento antes. Este duplica o descritor de arquivo 2 para ser uma cópia do descritor de arquivo 1 e obtemos:

Digite a descrição da imagem aqui

Ambos os streams foram redirecionados para o arquivo.

No entanto, tenha cuidado aqui! Escrevendo

comando> arquivo 2> & 1

não é o mesmo que escrever:

$ comando 2> & 1> arquivo

A ordem dos redirecionamentos é importante no Bash! Este comando redireciona apenas a saída padrão para o arquivo. O stderr ainda imprimirá no terminal. Para entender por que isso acontece, vamos percorrer as etapas novamente. Portanto, antes de executar o comando, a tabela do descritor de arquivos é semelhante a esta:

Digite a descrição da imagem aqui

Agora o Bash processa os redirecionamentos da esquerda para a direita. Primeiro, ele vê 2> & 1 para duplicar stderr para stdout. A tabela de descritores de arquivos se torna:

Digite a descrição da imagem aqui

Agora o Bash vê o segundo redirecionamento, >file , e redireciona o stdout para o arquivo:

Digite a descrição da imagem aqui

Você vê o que acontece aqui? Stdout agora aponta para o arquivo, mas o stderr ainda aponta para o terminal! Tudo o que é gravado no stderr ainda é impresso na canvas! Portanto, tenha muito cuidado com a ordem dos redirecionamentos!

Observe também que no Bash, escrevendo

$ comando &> arquivo

é exatamente o mesmo que:

$ command> & arquivo

Essa construção envia o stream de erro padrão ( stderr ) para a localização atual da saída padrão ( stdout ) – esse problema de moeda parece ter sido negligenciado pelas outras respostas.

Você pode redirect qualquer identificador de saída para outro usando esse método, mas é mais frequentemente usado para canalizar streams stdout e stderr em um único stream para processamento.

Alguns exemplos são:

 # Look for ERROR string in both stdout and stderr. foo 2>&1 | grep ERROR # Run the less pager without stderr screwing up the output. foo 2>&1 | less # Send stdout/err to file (with append) and terminal. foo 2>&1 |tee /dev/tty >>outfile # Send stderr to normal location and stdout to file. foo >outfile1 2>&1 >outfile2 

Note que este último não direcionará stderr para outfile2 – ele o redireciona para o stdout quando o argumento foi encontrado ( outfile1 ) e então redireciona stdout para outfile2 .

Isso permite alguns truques bastante sofisticados.

2>&1 é uma construção de shell POSIX. Aqui está um detalhamento, token por token:


2 : ” Erro padrão ” descritor de arquivo de saída.

>& : Duplicar um operador do Descritor de Arquivo de Saída (uma variante do operador Redirecionamento de Saída > ). Dado [x]>&[y] , o descritor de arquivo denotado por x é feito para ser uma cópia do descritor de arquivo de saída y .

1 descritor de arquivo de saída ” Saída padrão “.

A expressão 2>&1 copia o descritor de arquivo 1 para o local 2 , portanto, qualquer saída gravada como 2 (“erro padrão”) no ambiente de execução vai para o mesmo arquivo originalmente descrito por 1 (“saída padrão”).


Mais explicações:

Descritor de Arquivo : “Um inteiro único, não negativo, por processo, usado para identificar um arquivo aberto para fins de access a arquivos.”

Saída / erro padrão : Consulte a seguinte nota na seção Redirecionamento da documentação do shell:

Os arquivos abertos são representados por números decimais que começam com zero. O maior valor possível é definido pela implementação; no entanto, todas as implementações devem suportar pelo menos 0 a 9, inclusive, para uso pelo aplicativo. Esses números são chamados de “descritores de arquivos”. Os valores 0, 1 e 2 têm significados especiais e usos convencionais e estão implícitos em certas operações de redirecionamento; eles são referidos como input padrão, saída padrão e erro padrão, respectivamente. Os programas geralmente recebem sua input da input padrão e gravam a saída na saída padrão. Mensagens de erro geralmente são escritas em erro padrão. Os operadores de redirecionamento podem ser precedidos por um ou mais dígitos (sem a permissão de caracteres intermediários) para designar o número do descritor de arquivo.

Para responder à sua pergunta: Ela pega qualquer saída de erro (normalmente enviada para stderr) e grava na saída padrão (stdout).

Isso é útil com, por exemplo, ‘mais’ quando você precisa de paginação para todas as saídas. Alguns programas gostam de imprimir informações de uso no stderr.

Para ajudar você a lembrar

  • 1 = saída padrão (onde os programas imprimem saída normal)
  • 2 = erro padrão (onde os programas imprimem erros)

“2> & 1” simplesmente aponta tudo enviado para stderr, para stdout.

Eu também recomendo ler este post sobre o redirecionamento de erros onde este assunto é coberto em detalhes.

2 é o erro padrão do console.

1 é a saída padrão do console.

Este é o Unix padrão, e o Windows também segue o POSIX.

Por exemplo, quando você corre

 perl test.pl 2>&1 

o erro padrão é redirecionado para a saída padrão, para que você possa ver as duas saídas juntas:

 perl test.pl > debug.log 2>&1 

Após a execução, você pode ver toda a saída, incluindo erros, no debug.log.

 perl test.pl 1>out.log 2>err.log 

Em seguida, a saída padrão vai para out.log e o erro padrão para err.log.

Eu sugiro que você tente entender isso.

Do ponto de vista de um programador, isso significa exatamente isso:

 dup2(1, 2); 

Veja a man page .

Compreender que 2>&1 é uma cópia também explica porque …

 command >file 2>&1 

… não é o mesmo que …

 command 2>&1 >file 

O primeiro enviará os dois streams para o file , enquanto o segundo enviará os erros para o stdout e a saída ordinária para o file .

Pessoal, lembre – se sempre da dica do paxdiablo sobre a localização atual do alvo de redirecionamento … É importante.

Meu mnemônico pessoal para o operador 2>&1 é o seguinte:

  • Pense em & como significando 'and' ou 'add' (o personagem é um amperse não é?)
  • Assim, torna-se: ‘redirect 2 (stderr) para onde 1 (stdout) já / atualmente é e adicionar ambos os streams’ .

O mesmo mnemônico funciona para o outro redirecionamento freqüentemente usado, 1>&2 :

  • Pense em & significado and ou add … (você começa a idéia do e comercial, sim?)
  • Por isso, torna-se: ‘redirect 1 (stdout) para onde 2 (stderr) já / atualmente é e adicionar ambos os streams’ .

E lembre-se sempre: você precisa ler cadeias de redirecionamentos “do final”, da direita para a esquerda ( não da esquerda para a direita).

Desde que /foo não exista em seu sistema e /tmp faz…

 $ ls -l /tmp /foo 

irá imprimir o conteúdo de /tmp e imprimir uma mensagem de erro para /foo

 $ ls -l /tmp /foo > /dev/null 

irá enviar o conteúdo de /tmp para /dev/null e imprimir uma mensagem de erro para /foo

 $ ls -l /tmp /foo 1> /dev/null 

fará exatamente o mesmo (observe o 1 )

 $ ls -l /tmp /foo 2> /dev/null 

irá imprimir o conteúdo de /tmp e enviar a mensagem de erro para /dev/null

 $ ls -l /tmp /foo 1> /dev/null 2> /dev/null 

irá enviar tanto a listview como a mensagem de erro para /dev/null

 $ ls -l /tmp /foo > /dev/null 2> &1 

é taquigrafia

Isto é exatamente como passar o erro para o stdout ou o terminal.

Ou seja, o cmd não é um comando:

 $cmd 2>filename cat filename command not found 

O erro é enviado para o arquivo assim:

 2>&1 

Erro padrão é enviado para o terminal.

Redirecionando Entrada

O redirecionamento de input faz com que o arquivo cujo nome resulte da expansão da palavra a ser aberta para leitura no descritor de arquivo n, ou a input padrão (descritor de arquivo 0), se n não for especificado.

O formato geral para redirect a input é:

 [n] 

Redirecionando Saída

O redirecionamento de saída faz com que o arquivo cujo nome resulte da expansão da palavra a ser aberta para escrita no descritor de arquivo n, ou a saída padrão (descritor de arquivo 1), se n não for especificado. Se o arquivo não existir, ele será criado; se existir, será truncado para o tamanho zero.

O formato geral para redirect a saída é:

 [n]>word 

Mover descritores de arquivos

O operador de redirecionamento,

 [n]<&digit- 

move o dígito do descritor de arquivo para o descritor de arquivo n ou a input padrão (descritor de arquivo 0), se n não for especificado. dígito é fechado depois de ser duplicado para n.

Da mesma forma, o operador de redirecionamento

 [n]>&digit- 

move o dígito do descritor de arquivo para o descritor de arquivo n ou a saída padrão (descritor de arquivo 1) se n não for especificado.

Ref:

 man bash 

Digite /^REDIRECT para localizar a seção de redirection e saiba mais ...

Uma versão online está aqui: 3.6 Redirecionamentos

PS:

Muitas vezes, o man era a ferramenta poderosa para aprender Linux.

0 para input, 1 para stdout e 2 para stderr.

Uma dica : somecmd >1.txt 2>&1 está correto, enquanto somecmd 2>&1 >1.txt está totalmente errado sem efeito!