Os fluxos stdin, stdout e stderr são canais de comunicação estabelecidos automaticamente quando um comando é iniciado no Linux. Esses fluxos permitem que os scripts interajam com o sistema e outros programas, e também fornecem informações sobre como estão sendo usados: se a entrada ou saída está sendo redirecionada.
Fluxos: Pontos de Conexão de Dados
Ao iniciar sua jornada no aprendizado do Linux ou de sistemas operacionais semelhantes ao Unix, você se deparará com os termos stdin, stdout e stderr. Estes são, na verdade, três fluxos de dados padrões, criados quando um comando do Linux é acionado. Na computação, um fluxo é uma via por onde dados são transferidos. Nesses fluxos específicos, os dados transmitidos são, geralmente, texto.
Esses fluxos, assim como rios, possuem duas extremidades: uma fonte e um destino. Todo comando do Linux utilizado providencia uma das extremidades de cada fluxo. A outra extremidade é definida pelo shell que executou o comando. Esta extremidade se ligará ao terminal, a um pipe ou será redirecionada para um arquivo ou outro comando, dependendo da linha de comando que iniciou o comando original.
Os Fluxos Padrão no Linux
No Linux, o stdin é o fluxo de entrada padrão. Ele aceita texto como entrada para o comando. A saída de texto gerada pelo comando é entregue ao shell através do fluxo stdout (saída padrão). As mensagens de erro do comando são enviadas através do fluxo stderr (erro padrão).
Portanto, existem dois fluxos de saída, stdout e stderr, e um fluxo de entrada, stdin. Ao separar as mensagens de erro e a saída normal em canais distintos, é possível tratá-los de forma independente.
Fluxos Tratados como Arquivos
No Linux, os fluxos, assim como grande parte dos elementos do sistema, são tratados como arquivos. Assim como você lê texto de um arquivo e escreve texto em outro, esses fluxos de dados seguem a mesma lógica. A manipulação de fluxos como arquivos é uma analogia natural.
Cada arquivo associado a um processo recebe um identificador único, chamado descritor de arquivo. Sempre que uma ação precisa ser realizada em um arquivo, o descritor de arquivo é utilizado para identificar o arquivo. Os valores utilizados para os fluxos stdin, stdout e stderr são sempre os seguintes:
0: | stdin |
1: | stdout |
2: | stderr |
Reagindo a Redirecionamentos e Pipes
Ao introduzir um assunto complexo, é comum simplificá-lo para facilitar o entendimento. Na gramática, por exemplo, aprende-se uma regra que parece simples, mas que possui mais exceções do que exemplos que a seguem. O mesmo acontece com a afirmação de que um processo não tem ciência de onde seus fluxos padrão terminam.
Mas na realidade, um processo consegue saber, ou ao menos pode descobrir, se sua saída está sendo direcionada para o terminal ou para um arquivo. Ele pode identificar se sua entrada vem do teclado ou de um pipe. Caso o desenvolvedor deseje, essa informação pode ser usada para alterar o comportamento do software.
Podemos observar essa mudança de comportamento facilmente. Teste estes dois comandos:
ls
ls | cat
O comando ls
se comporta de maneira diferente quando sua saída (stdout) é redirecionada para outro comando. A mudança para uma saída de coluna única é feita pelo próprio comando ls
, e não por cat
. O mesmo ocorre quando a saída de ls
é redirecionada para um arquivo:
ls > capture.txt
cat capture.txt
Redirecionando stdout e stderr
A separação das mensagens de erro em um fluxo dedicado oferece uma vantagem: podemos redirecionar a saída de um comando (stdout) para um arquivo e, ainda assim, receber as mensagens de erro (stderr) no terminal. Isso nos permite reagir aos erros em tempo real. Além disso, evita que as mensagens de erro se misturem com a saída normal redirecionada para o arquivo.
Crie um arquivo chamado error.sh
com o seguinte conteúdo:
#!/bin/bash echo "Tentando acessar um arquivo que não existe" cat bad-filename.txt
Torne o script executável:
chmod +x error.sh
A primeira linha do script envia um texto para o terminal, através do fluxo stdout. A segunda linha tenta acessar um arquivo inexistente, gerando uma mensagem de erro que é enviada via stderr.
Execute o script:
./error.sh
Como podemos observar, ambos os fluxos de saída, stdout e stderr, foram exibidos no terminal.
Vamos tentar redirecionar a saída para um arquivo:
./error.sh > capture.txt
A mensagem de erro, enviada via stderr, continua sendo mostrada no terminal. Verificando o arquivo, vemos que a saída stdout foi redirecionada corretamente.
cat capture.txt
A saída stdout foi redirecionada para o arquivo, como esperado.
O símbolo de redirecionamento “>” funciona com stdout por padrão. Para indicar qual fluxo de saída você quer redirecionar, utilize os descritores de arquivo numéricos.
Para redirecionar explicitamente o stdout, use:
1>
Para redirecionar explicitamente o stderr, use:
2>
Testaremos novamente, usando “2>” desta vez:
./error.sh 2> capture.txt
A mensagem de erro é redirecionada e a mensagem stdout é enviada para o terminal.
A mensagem stderr está no arquivo capture.txt
, conforme o esperado.
Redirecionando stdout e stderr
Se podemos redirecionar stdout ou stderr para arquivos independentemente, é natural que seja possível redirecionar ambos ao mesmo tempo, para arquivos distintos.
E é exatamente isso que podemos fazer. O comando a seguir direciona stdout para um arquivo chamado capture.txt
e stderr para um arquivo chamado error.txt
.
./error.sh 1> capture.txt 2> error.txt
Como ambos os fluxos de saída (saída padrão e erro padrão) são redirecionados, nenhuma saída é exibida no terminal. Retornamos ao prompt como se nada tivesse acontecido.
Verificamos o conteúdo de cada arquivo:
cat capture.txt
cat error.txt
Redirecionando stdout e stderr para o mesmo arquivo
Agora, podemos redirecionar cada fluxo de saída para seu próprio arquivo. A última combinação que podemos fazer é enviar stdout e stderr para o mesmo arquivo.
Isso é possível com o seguinte comando:
./error.sh > capture.txt 2>&1
Vamos detalhar:
./error.sh
: Executa o script error.sh
.> capture.txt
: Redireciona o fluxo stdout para o arquivo capture.txt
. O símbolo >
é um atalho para 1>
.2>&1
: Usa a instrução de redirecionamento >&
, que diz ao shell para direcionar um fluxo para o mesmo destino de outro. Nesse caso, dizemos “redirecione o fluxo 2, stderr, para o mesmo destino que o fluxo 1, stdout”.
Não há saída visível, o que é encorajador.
Vamos verificar o arquivo capture.txt
:
cat capture.txt
Os fluxos stdout e stderr foram redirecionados para o mesmo arquivo.
Para que a saída de um fluxo seja redirecionada e descartada, envie-a para /dev/null
.
Detectando Redirecionamento em um Script
Vimos que um comando pode identificar se seus fluxos estão sendo redirecionados. Mas, podemos fazer o mesmo em nossos scripts? Sim. E a técnica é simples de usar.
Crie um arquivo chamado input.sh
com o seguinte conteúdo:
#!/bin/bash if [ -t 0 ]; then echo stdin vindo do teclado else echo stdin vindo de um pipe ou arquivo fi
Torne o script executável:
chmod +x input.sh
A parte importante está no teste dentro dos colchetes. A opção -t
(terminal) retorna verdadeiro (0) se o arquivo associado ao descritor de arquivo for um terminal. Usamos o descritor de arquivo 0 como argumento para o teste, que representa o stdin.
Se stdin estiver conectado a um terminal, o teste é verdadeiro. Se stdin estiver conectado a um arquivo ou pipe, o teste falha.
Use qualquer arquivo de texto como entrada para o script. Usaremos um arquivo chamado dummy.txt
.
./input.sh < dummy.txt
O script reconhece que a entrada não vem do teclado, mas de um arquivo. Podemos usar essa informação para alterar o comportamento do script.
Agora testaremos com um pipe.
cat dummy.txt | ./input.sh
O script reconhece que sua entrada vem de um pipe. Ou, mais precisamente, que o fluxo stdin não está conectado ao terminal.
Vamos executar o script sem redirecionamento ou pipe:
./input.sh
O fluxo stdin está conectado ao terminal, e o script informa isso.
Para verificar o mesmo com o fluxo de saída, precisamos de um novo script. Crie um arquivo chamado output.sh
com o seguinte conteúdo:
#!/bin/bash if [ -t 1 ]; then echo stdout vai para o terminal else echo stdout está sendo redirecionado ou canalizado fi
Torne-o executável:
chmod +x input.sh
A única mudança nesse script está no teste entre colchetes. Usamos o dígito 1, que representa o descritor de arquivo para stdout.
Vamos testar. Redirecionaremos a saída através do cat
:
./output.sh | cat
O script identifica que sua saída não está indo diretamente para um terminal.
Também podemos testar o script redirecionando a saída para um arquivo:
./output.sh > capture.txt
Não há saída no terminal, e voltamos ao prompt silenciosamente, como esperado.
Podemos analisar o arquivo capture.txt
:
cat capture.txt
Mais uma vez, o teste simples detecta que o fluxo stdout não está sendo direcionado para um terminal.
Se executarmos o script sem redirecionamento ou pipe, ele detectará que stdout está sendo direcionado para o terminal.
./output.sh
E é exatamente isso que vemos.
Fluxos de Consciência
Saber como determinar se seus scripts estão conectados ao terminal, a um pipe ou se estão sendo redirecionados, permite adaptar seu comportamento. As saídas de log e diagnóstico podem ser mais detalhadas dependendo se a saída vai para a tela ou para um arquivo. As mensagens de erro podem ser registradas em um arquivo separado da saída normal do programa.
Como é comum, mais conhecimento leva a mais opções.