# UNIX Pipes

Para entendermos UNIX *pipes*, devemos primeiro recapitular o seguinte exemplo:

```bash
$ echo 'my precious' > rawcontent.txt
$ base64 < rawcontent.txt
bXkgcHJlY2lvdXMK
```

* O programa `echo` redireciona a o **output** para o arquivo `rawcontent.txt`
* O conteúdo do arquivo é enviado como **input** para o comando `base64`

Note o padrão aqui: temos uma *pipeline* de transformação de dados, onde o **output de um programa é utilizado como input** para o próximo programa.&#x20;

Esta técnica de pipeline é utilizada em sistemas UNIX-like com o operador *pipe* `|`.

## UNIX pipelines

Ao invés de escrevemos nossa pipeline em múltiplas linhas, dificultando a legibilidade caso a complexidade aumente, podemos utilizar o operador `|` para montar uma sentença única de comandos encadeados:

```bash
$ echo 'my precious' | base64
bXkgcHJlY2lvdXMK
```

Muito melhor, não? E muito provável você já viu isto em algum lugar, por exemplo:

```bash
$ ps ax | grep ruby
88327 s002  S+     0:00.94 docker run -it ruby irb
88330 s002  S+     0:00.92 /usr/local/bin/com.docker.cli run -it ruby irb
91074 s003  S+     0:00.00 grep ruby
```

O **output** do comando `ps` é enviado como **input** para o comando `grep`. *Cool, uh?* Este pipe é chamado de *pipe anônimo, ou* **anonymous pipe**!

### Anonymous pipe

Este pipe é chamado de **anônimo** justamente por não ter nome e ser temporário, pois o *fd* é criado durante a pipeline e depois é liberado.

Este tipo de **IPC** utiliza uma comunicação tem as seguintes características:

* **One-way**, ou seja, a informação trafega apenas e uma única direção
* **FIFO** (first-in, first-out), ou seja, o output é redirecionado para um pipe e **enfileirado** como input em outro pipe do próximo comando

```bash
$ ps ax | grep docker | tail -n 3

62374 s039  S+     0:05.31 /usr/local/bin/com.docker.cli run -it ubuntu bash
65442 s040  S+     0:02.93 docker run -it ubuntu bash
65445 s040  S+     0:02.86 /usr/local/bin/com.docker.cli run -it ubuntu bash
```

Quando um `|` é criado, abre-se um par de *file descriptors*, uma para escrita e outro para leitura, tal como fizemos na seção anterior com *custom fd*.

Como a pipe é anônima, ambos file descriptors abertos são utilizados apenas no contexto da pipeline e são automaticamente liberados/fechados quando a **pipeline** termina.

Apesar de pipes anônimas `|` serem utilizadas praticamente em quase tudo, é possível criamos pipes **com nomes**?

### Named pipes

Como o próprio nome diz, **named pipes** são pipes *com nomes*. São similares a pipes anônimas; empregam **FIFO** e são uma forma de IPC de via única (**one-way**).

A única diferença é que um named pipe é criado de forma explícita via comando `mkfifo`, onde um arquivo é criado no *filesystem* e aberto para escrita e leitura.

```bash
$ mkfifo myqueue
```

Um arquivo chamado `myqueue` é criado.&#x20;

Vamos enviar uma mensagem para o pipe com o comando `echo`, utilizando redirecionamento de stream `>` que vimos na última seção:

```bash
$ echo 'my precious' > myqueue 
```

Note que o processo fica bloqueado, à espera de algo.

#### IPC one-way

Por ser uma estrutura de fila *FIFO* simples e ser utilizado como *one-way IPC*, o sistema operacional precisa garantir que a mensagem **será recebida por outro processo.** Por isso o processo *escritor*, ou **writer**, fica bloqueado, pois é preciso que outro processo outro processo *leitor* (**reader**) para "consumir" a mensagem do pipe.&#x20;

Em outra sessão do bash, vamos consumir a informação do pipe utilizando o comando `cat`:

```bash
$ cat myqueue
my precious
```

Yay!&#x20;

O mesmo acontece se iniciarmos com o *leitor*: este fica bloqueado à espera que alguma mensagem chegue no pipe, no caso através de outro processo *escritor*.&#x20;

### Implementando um simples Background Job com UNIX pipes

Utilizando **anonymous pipes e named pipes**, podemos explorar a funcionalidade primitiva de um sistema de processamento assíncrono (**background job**).

Começamos por definir os componentes:

1. Um processo *leitor*, ou **consumer**, fica infinitamente à espera de mensagens no *pipe* (fila)
2. Diferentes *escritores*, ou **publishers**, colocam mensagens no pipe de forma assíncrona
3. O processo leitor (**consumer**) recebe cada mensagem no pipe e faz o devido **processamento da mensagem**

:point\_right: Nosso background job irá fazer a simples tarefa de receber uma mensagem **codificada** em *base64*, **decodificá-la**, e então mostrar no screen (STDOUT).

#### **Consumer**

Primeiro, criamos o pipe que representará a "fila" do nosso background job:

```bash
mkfifo myqueue
```

Agora, o consumer fica em **loop infinito** à espera de mensagens na fila.&#x20;

Dentro do *loop*, consome a mensagem, decodifica-a e então mostra no screen:

```bash
while true
do
  ## Fica bloqueado à espera da próxima mensagem na fila
  ENCODED=`cat myqueue` 
  
  ## Quando a mensagem chega na fila, decodifica-a utilizando o comando
  ##  echo e base64 com anonymous pipes
  DECODED=`echo $ENCODED | base64 -d`
  
  echo "Mensagem decodificada: $DECODED"
done
```

E o consumer está pronto. Código final do arquivo `consumer.sh`:

```bash
#!/bin/bash

## Cria o named pipe
mkfifo myqueue

echo 'Aguardando jobs na fila...'

while true
do
  ## Fica bloqueado à espera da próxima mensagem na fila
  ENCODED=`cat myqueue` 
  
  ## Quando a mensagem chega na fila, decodifica-a utilizando o comando
  ##  echo e base64 com anonymous pipes
  DECODED=`echo $ENCODED | base64 -d`
  
  echo "Mensagem decodificada: $DECODED"
done
```

Em uma sessão do bash:

```bash
$ bash consumer.sh
Aguardando jobs na fila...
```

E em outra sessão do bash, podemos utilizar vários *producers* para enviar diversas mensagens codificadas para a fila:

```bash
$ echo 'my precious' | base64 > myqueue
$ echo 'pipes are awesome' | base64 > myqueue
```

Consultando o output na sessão do consumer:

```bash
Mensagem decodificada: my precious
Mensagem decodificada: pipes are awesome
```

### Resumo

Que jornada! Nesta seção vimos a utilização de **UNIX pipes** para IPC, vimos a semelhança e diferença entre anonymous pipes `|` e named pipes, bem como a implementação de um **sistema de background jobs** com pipes.

Agora, é hora de explorar uma forma ainda mais sofisticada de IPC: **UNIX Sockets**.
