Se voltarmos um pouco para os conhecimentos básicos da informática, podemos lembrar que existem dois tipos principais de interfaces entre o usuário e o computador: interface de entrada (teclado, mouse) e interface de saída (monitor). Para entender o funcionamento das entradas e saídas dos comandos, basta inicialmente utilizar essa mesma definição: o teclado é a entrada padrão (STDIN), a tela é a saída padrão (STDOUT e STDERR).
Por exemplo, quando eu digito o comando ls:
$ ls arquivo1 arquivo2 arquivo3
Ele me retornou como saída na tela a lista dos arquivos: arquivo1, arquivo2, arquivo3. O comando retornou na tela pois não especificamos nada. Podemos fazer a mesma coisa com o comando cat:
$ cat contato.txt ...Informações de Contato...
Neste caso, o comando cat recebeu um argumento (contato.txt), o qual podemos chamar de entrada. Como a entrada padrão é o teclado, tivemos que digitar o nome do arquivo contato.txt. Recebido o argumento, o cat mandou para a tela (saída padrão), o conteúdo do arquivo contato.txt.
Alterando a saída dos comandos
Para alterar a saída dos comandos utilizamos os sinais de “>” e “>>”. O sinal de “>” altera a saída padrão, sobrescrevendo o destino, enquanto o sinal de “>>” altera a saída padrão mas adiciona o conteúdo ao destino. Se considerarmos estes caracteres como “redirecionadores”, talvez possamos entender melhor.
Por exemplo, se eu quiser alterar a saída do comando ls anterior, para ao invés de aparecer na tela, ser gravada em um arquivo:
$ ls > lista_de_arquivos.txt
Neste caso, ao invés de mostrar na tela, a saída agora é o arquivo lista_de_arquivos.txt e por isso o comando ls vai escrever neste arquivo. Se olharmos o conteúdo, veremos a lista “arquivo1, arquivo2, arquivo3”.
Como utilizamos o “>”, se existisse o arquivo lista_de_comandos.txt, ele seria sobrescrito pelo comando, perdendo todo seu conteúdo anterior. Utilizando o “>>”, podemos gravar várias linhas consecutivas sem sobrescrever os conteúdos anteriores:
$ ls >> lista_de_arquivos.txt
Se executarmos este comando várias vezes, veremos que dentro do arquivo haverá várias linhas com a mesma listagem de arquivos do comando ls.
Um uso muito comum disto é redirecionar a saída para o /dev/null, que é um dispositivo/arquivo especial que anula tudo que vai para ele. Ao fazer:
$ cat contato.txt > /dev/null
A informação que está dentro do arquivo contato.txt não vai para nenhum lugar: nem para a tela, nem para um arquivo.
Existem dois tipos de saída: STDOUT (Saída Padrão) que corresponde às mensagens normais de um comando, como por exemplo, a listagem dos arquivos do comando ls. O STDOUT pode ser representado como estávamos fazendo anteriormente, com um sinal de “>”, ou também por “1>”. Então os dois comandos a seguir fazem a mesma coisa:
$ cat contato.txt > /dev/null $ cat contato.txt 1> /dev/null
O STDERR (Saída de Erros Padrão) corresponde às mensagens de erro que o programa gera. Seguindo os mesmos exemplos anteriores, vemos o STDERR em ação quando tentamos listar um arquivo que não existe:
$ ls arquivonaoexistente > /dev/null ls: não foi possível acessar arquivonaoexistente: Arquivo ou diretório não encontrado
Apesar de redirecionarmos a saída para ser anulada (/dev/null), mesmo assim o ls retornou na tela a mensagem de que o arquivo não existe. Isto acontece porque esta é uma mensagem de erro e por isso não é contemplada pelo sinal de “>” ou “1>”. Para redirecionar o STDERR, utilizamos “2>”. Corrigindo o exemplo anterior:
$ ls arquivonaoexistente > /dev/null 2> /dev/null
Agora sim, tanto o STDOUT quanto o STDERR serão anulados, pois foram redirecionados para /dev/null.
Como redirecionamos as duas saídas para um lugar só, também podemos simplificar o comando anterior da seguinte forma:
$ ls arquivonaoexistente > /dev/null 2>&1
Vamos por partes: primeiro redirecionei a saída padrão para /dev/null (> /dev/null) e logo em seguida redirecionei a saída de erros para o mesmo lugar que a saída padrão (2>&1). O caracter especial “&” atuou como uma referência para a saída padrão. Se não houvesse o “&”, a saída de erros seria direcionada para um arquivo comum chamado “1”. Por fim, este comando também faz com que os dois tipos de saída sejam enviados para /dev/null e, por consequência, anulados.
Alterando a entrada dos comandos
Para alterar a entrada dos comandos, utilizamos o sinal de “<”. Geralmente é utilizado em comandos que necessitam de arquivos como argumentos. Por exemplo, o comando sort ordena as linhas de um arquivo alfabeticamente e sua sintaxe é sort arquivo. Podemos redirecionar o conteúdo do arquivo diretamente, sem passá-lo como argumento:
$ sort < arquivo
Cada linha de arquivo será passada para o comando sort, que ordenará e mostrará na tela. Vamos então redirecionar essa saída para outro arquivo:
$ sort < arquivo > arquivo_ordenado
Outro exemplo muito comum do uso da alteração do STDIN é quando um programa necessita de sub-comandos. Por exemplo, a shell bash, quando executada, fornece um prompt para o usuário digitar os comandos via teclado. Podemos automatizar essa digitação colocando todos os comandos em um arquivo. Chamaremos este arquivo de comandos.txt, com o conteúdo:
cd /root ls cd /usr/ ls cd /etc cat /etc/passwd cd /usr/local/bin pwd
Agora podemos executar uma shell, passando como entrada este arquivo:
$ bash < comandos.txt
Ao invés do bash fornecer um prompt para a digitação de comandos, ele irá ler e executar todo o conteúdo do arquivo comandos.txt, depois sair. Este é o conceito de shell-script, mas utilizado de uma forma mais crua. Apesar de ser útil, geralmente usamos uma forma diferente para criar shell-scripts.
Outro exemplo como o anterior, mas bastante usado, seria restaurar um arquivo do banco de dados MySQL de forma automática. Nesta situação, temos um arquivo chamado base.sql que contém vários comandos SQL de criação de tabelas e dados. Usa-se então o redirecionador de entrada para que ao invés do usuário digitar todos estes comandos, eles serem passados para o utilitário do MySQL:
$ mysql bancodedados < base.sql
Utilizando o Pipe
Além dos redirecionadores, temos também o pipe, representado pelo caracter “|”. O pipe é responsável por passar a saída de um comando como a entrada de outro. Em outras palavras, ao se executar um comando, ao invés da saída dele ir para a tela ou para um arquivo, ele se torna a entrada de outro comando, funcionando de forma parecida com a utilização tanto do “>” quanto do “<”.
Utilizando um exemplo parecido com os anteriores, vamos ordenar um arquivo texto:
$ cat arquivo.txt | sort
O comando cat, ao invés de mostrar o conteúdo de arquivo.txt na tela, manda a saída para o comando sort, que ordena e joga na tela. O mesmo comando, mas agora redirecionando o resultado para um outro arquivo:
$ cat arquivo.txt | sort > arquivo_ordenado.txt
Ou então, se quisermos ver uma listagem detalhada de um diretório com bastante coisa, podemos facilitar a visualização combinando a listagem e o comando less:
$ ls -lha /usr/bin | less
Ao listar todos os arquivos do diretório /usr/bin, o ls manda o resultado para o less. Com isso, podemos utilizar os recursos de paginação do less. Vamos agora descobrir se um processo está sendo executado no sistema:
$ ps aux | grep bash
O comando ps listou todos os processos do sistema e enviou para o grep “filtrar” e só mostrar os que tem a palavra bash.
Podemos utilizar também vários pipes, como no exemplo:
$ cat /etc/passwd | cut -d “:” -f1 | sort | uniq
Utilizamos 4 comandos, cada um recebendo o resultado do outro. Vamos por passos:
- cat /etc/passwd: Simplesmente mostra o conteúdo do arquivo /etc/passwd.
- cut -d “:” -f1: Recebe o conteúdo do arquivo /etc/passwd, enviado pelo cat, e separa seus “campos”, mostrando apenas a primeira palavra de cada linha (usuário).
- sort: Organiza esses usuários em ordem alfabética.
- uniq: Remove linhas duplicadas.
Por fim, o uniq manda para a tela o resultado final: a lista dos usuários do sistema, ordenados por ordem alfabética.