Introdução
Segundo a filosofia
Unix, cada comando foi criado para executar uma única tarefa e da melhor forma possível.
A ideia por trás de cada comando, é que eles tivessem canais de:
- Entrada (input): cuja entrada padrão (standard input ou stdin) é o teclado;
- Saída (output): cuja saída padrão (standard output ou stdout) é o terminal;
- Erro (error): cujo erro padrão (standard error ou stderr) também é o terminal.
Dizemos padrão, pois há outros meios onde as informações podem passar, como arquivos, impressoras, etc.
Os
pipes servem para fazer as informações 'correrem' entre os arquivos, canalizando as saídas de um arquivo para a entrada de outros, e caso haja algum problema, a informação irá para o canal de erro, e como cada comando tem seu erro, é possível descobrir onde aconteceu o problema.
Chamamos estas vias de
Informação de Pipelines, que são totalmente flexíveis aos nossos propósitos.
São ideias simples, mas o resultado é extremamente poderoso, pois foi possível fazer com que os comandos tivessem uma comunicação interna, criando sistemas mais complexos, que executassem tarefas que dependessem cada vez menos dos humanos.
Em programação, de um modo geral, coisas grandes e complexas são várias coisas pequenas, com funções bem definidas, trabalhando juntas, de forma coesa e estável.
Esta prática, de fazer coisas pequenas e bem definidas, não deve servir apenas de estudo por sua parte, deve fazer parte de suas práticas na hora de programar/criar/pensar.
Os benefícios são muitos, como:
- Organização;
- Fácil identificação de bugs;
- Fácil manutenção de código, etc.
Mas se o Unix funciona assim, você tem alguma dúvida de que é uma boa prática?
P.S.: Antes de prosseguir, recomendo total atenção na hora de digitar os comandos, e indico fortemente que você crie um diretório com seus arquivos para teste. Pois é muito fácil e provável que algum erro ocorra, e você venha a apagar ou mudar informações importantes de algum arquivo do sistema (sim, já aconteceu comigo, mas não quero falar sobre isso, ainda dói).
Backup também!
Redirecionamento
Todo processo do Unix possui três arquivos abertos (file descriptors, arquivos de descrição), o de entrada, o de saída e o de erro:
- O file descriptor é um número que se refere a um arquivo aberto;
- O 0 refere-se a stdin;
- O 1 a stdou;
- E 2 a stderr.
Ao invés de usar as entradas e saídas padrões, podemos redirecioná-las para outros arquivos (outros, pois teclado e terminal são considerados arquivos também. Aliás, em Unix, o que não é um arquivo?).
Redirecionando a saída padrão
Ao invés da saída ser exibida na tela, ela irá para um arquivo:
comando > arquivo_de_saida
Por exemplo, vamos salvar os arquivos da sua pasta de executáveis:
ls /etc/bin > bin.txt
Para ver:
cat bin.txt | more
Redirecionando a entrada padrão
Ao invés da entrada ser o teclado, ela pode ser um arquivo.
Por exemplo, vamos usar o comando
wc, que calcula o número de linhas, de palavras e o tamanho em Bytes para descobrir a quantidade de comandos que você pode usar a partir de sua pasta "bin":
wc < bin.txt
A resposta é o primeiro elemento.
Redirecionando o erro padrão
A maior utilidade do sistema gerar erros e tratá-los como arquivos, é que você pode redirecioná-los.
Por exemplo, enquanto um processo é direcionado com sua saída para um arquivo (de log, por exemplo), o erro pode pode ser direcionado para o terminal.
Assim, você vê na tela os erros enquanto o processo está a ocorrer em background (provavelmente você já notou que esta é uma prátia comum. Pelo fato de tudo ter um canal de erro, eles são muitos e bem específicos, o que é excelente, pois lhe deixa com total controle sobre o que está acontecendo no sistema. Os desenvolvedores investiram muito nisso).
Para redirecionar o erro, use o
file descriptor 2 antes do '>'. Veja:
ls /pasta/inexistente 2> erro.log
Depois, veja o conteúdo de "erro.log":
cat erro.log
Deve mostrar a mensagem de erro, informando que não existe tal diretório ou arquivo.
Redirecionando saída e erro padrão
Se você quer direcionar sempre o erro pro mesmo lugar da saída, use:
ls /usr/bin > bin.txt 2>&1
Interprete na ordem. Primeiro o comando passa os dados pra saída, que é o arquivo "bin.txt". Caso surja algum erro, ele é envidado (2>) para '&1'.
E onde é esse &1? '&1', guarda o local da saída, por isso o "1" (lembre-se: stdin 0, stdout 1 e stderr 2), seja lá onde for (nem sempre sabemos).
Não coloque espaços ao redor de '2>&1'. Escreva tudo junto mesmo, pois o '&' é outro comando.
Um atalho para redirecionar ambos, saída e erro, para um arquivo só, é usar '&>':
ls /diretorio/inexistente &> ambos.log
Anexando informações
Quando se usa o operador '>' para saída, devemos tomar muito cuidado, pois se houver um arquivo com o mesmo nome, ele simplesmente substitui, sem aviso prévio.
Se você quiser criar um arquivo de registro (log), use o operador '>>' que anexa, sempre, ao fim do arquivo, as novas informações. E caso o arquivo não exista, ele cria o tal.
Vamos testar com o comando
uptime, que dentre outras funções, informa a hora atual e há quanto tempo seu computador está ligado:
uptime >> time.log
$ uptime >> time.log
$ cat time.log
Deletando
Para deletar o conteúdo do arquivo, usa ':>':
:> bin.txt
Note que ele deletou o conteúdo, mas o "bin.txt" ainda continua lá.
* Muito cuidado para não digitar o nome errado do arquivo.
/dev/null : descartando a saída
Muitas vezes não queremos ver o conteúdo de uma saída, como o resultado do
ls, queremos ver apenas se ele gera erro, para testar um comando ou testar uma condição em Shell Scripting.
Para tal, redirecione sua saída para "/dev/null":
ls /usr/bin > /dev/null