1.7 - Conectando Programas
Uma das capacidades mais incríveis do Bash
é a forma como é possível manipular a entrada e saída dos programas e conectá-las de diversas formas para redefinir o comportamento padrão dos comandos. Me permita explicar melhor antes, como o Shell lida com a entrada e saída dos comandos.
1.7.1 Streams de Entrada e Saída (Input/Output Streams)
No Shell, programas possuem dois streams primários associados a eles: o stream de entrada (input), e o stream de saída (output). Quando o programa tenta ler a entrada, ele lê do stream de entrada, e quando ele imprime algo, ele imprime no stream de saída. Normalmente, os streams de entrada e saída de um programa são o seu terminal (quando rodando a partir do shell). Ou seja, seu teclado (à medida que você digita no shell) e a janela do terminal na sua tela. Contudo, nós podemos também religar estes streams de outras formas.
Important
Streams em computação, é um termo comum usado para designar um fluxo de dados que não acontece de uma única vez, mas que é feito de forma contínua, ao longo do tempo, em pequenos lotes.
Aqui estamos falando de Streams sendo usados para o fluxo contínuo de dados de entrada e de saída de um comando ou programa executado no shell. Mas o conceito é muito comum em diversas outras áreas da computação, como na leitura/escrita de arquivos no disco, ou obtendo/enviando dados através da rede/internet.
O termo ficou ainda mais popular com a transmissão de conteúdo online como chamadas de vídeo ou transmissões audiovisuais em lives na internet.
1.7.2 Religamento de Streams
A forma mais simples de religar streams no Bash
é através dos operadores >
(para religar o stream de saída do programa) e <
(para religar o stream de entrada do programa). Vamos ver alguns exemplos:
Note que o comando ls
acima não imprimiu a saída no terminal como de costume. Por outro lado, você pode conferir um novo arquivo criado no diretório ~/teste
chamando ls.txt
. Você pode usar o comando cat
(que imprime o conteúdo de um arquivo no terminal) e você irá notar que o seu conteúdo é a saída do comando ls
que teria sido impressa no terminal se não a tivéssemos religado.
Uma forma de conferir o conteúdo do arquivo ~/teste/ls.txt
, é o utilizando como stream de entrada do comando cat
, que imprime o stream de entrada no stream de saída. Ao religar o stream de entrada do comando cat
usando o arquivo ~/teste/ls.txt
sem religar o stream de saída, ele irá imprimir o conteúdo no terminal.
O comando cat
, quando não especificado nenhum argumento, usa o terminal como stream de entrada (capturando tudo o que o usuário digitar) e as imprime no stream de saída (que também é o próprio terminal, imprimindo exatamente o que é digitado), à medida que os dados são enviados (normalmente, sempre que uma linha é finalizada). O resultado padrão do comando cat
, é ter suas linhas repetidas, uma vindo pela entrada, e logo em seguida sendo impressa na saída.
cat # demonstrando os dados digitados no stream de entrada, e impressos na saída imediatamente depois
Por exemplo, é possível usar o cat como um editor de um novo arquivo, que será criado usando a religação do stream de saída, digitando o conteúdo a partir do stream de entrada.
Note
Para encerrar a leitura da entrada, nós usamos o comando Ctrl+C. Isto envia um sinal para o processo que está executando o comando cat
, orientando-o a parar.
Existem outras formas de enviar sinais aos processos quando estamos executando programas no bash, mas veremos isto no futuro.
Um outro operador útil é o operador >>
. Ele tem, basicamente o mesmo efeito do operador >
, que religa o stream de saída, com exceção de que, se a saída é redirecionada a um arquivo que já existe, ele concatena a saída do programa atual ao conteúdo original do arquivo, uma operação popularmente conhecida na computação como append
. Ou seja, o conteúdo original do arquivo é mantido, e o novo conteúdo é enviado ao final dele.
Note que o conteúdo original do arquivo.txt
foi mantido, e o resultado do comando ls
foi inserido depois da última linha original.
Existe um operador que conecta a saída de um comando do bash à entrada de outro comando, criando uma cadeia de comandos, ou "esteira" (pipeline), de etapas pelas quais os dados resultantes de um programa são aplicados ao outro, sendo transformados até produzirem uma saída desejada. Este operador é o |
(pipe).
Aqui temos uma pipeline que faz a seguinte sequência:
- ls -lha ~
- lista todos os arquivos da pasta home
, em formato de lista, com tamanhos humanamente legíveis, e inclindo os arquivos ocultos (que começam com .
)
- grep Jul
- o comando grep
filtra as linhas vindas da entrada de acordo com algum padrão (no caso, Jul
). Basicamente ele pega o resultado do ls
e separa somente os arquivos modificados em Julho
- head -5
- O comando head
toma somente as primeiras linhas da entrada. Por padrão, ele toma somente as primeiras 10, mas a opção -5
faz com que ele pegue somente as primeiras 5 e ignore as demais linhas.
- tee /<caminho>
- O comando tee
é um comando similar ao comando cat
, com a diferença que, além de imprimir no stream de saída, ele também escreve o arquivo. No caso acima, ele escreve no terminal e no arquivo /home/dmyoko/teste/pipeline.txt
.
- Por exemplo, é possível criar dois arquivos a partir do comando tee
:
ls -lha ~ | grep Jul | head -5 | tee /home/dmyoko/teste/pipeline.txt > /home/dmyoko/teste/pipeline2.txt
- A linha acima instrui instrui o
tee
a escrever a entrada no arquivo/home/dmyoko/teste/pipeline.txt
e religa o stream de saída para o arquivo/home/dmyoko/teste/pipeline2.txt
usando o operador>
. - Imagine talvez, usar o operador
>>
nesta linha, e qual será o resultado. Como isso iria diferir ambos os arquivos.
Nós iremos usar muito o operador de pipe (|
) na aula em que iremos tratar de Manipulação de Dados no Shell.