# File descriptors

File descriptor (ou **fd**) é um arquivo especial que é organizado em uma *file descriptor table* e possui um **identificador único,** que aponta uma referência seja para um dispositivo físico I/O, um arquivo ou até mesmo outro *file descriptor*.

### Standard streams

Todo processo UNIX tem por default 3 *communication channels*, também conhecidos por **standard streams**, que são representados por file descriptors (fd):

* `fd 0`: standard input, ou **STDIN**
* `fd 1`: standard output, ou **STDOUT**
* `fd 2`: standard error, ou **STDERR**

<figure><img src="https://1859591191-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8ipiROc4LnGxp3JLLiCL%2Fuploads%2FGLIPNLfBtdVawRo0uorN%2FScreenshot%202022-10-04%20at%2000.49.30.png?alt=media&#x26;token=6af89546-1870-4219-bb7a-913c54e19b1c" alt=""><figcaption></figcaption></figure>

#### STDOUT

Vamos analisar o seguinte comando:

```bash
$ echo 'Hello'
Hello
```

* O programa `echo` envia uma mensagem para o **STDOUT**, ou seja, *file descriptor 1*
* STDOUT refere-se ao screen do computador, e por isto a mensagem `Hello` é mostrada na tela

#### STDIN

Agora, vamos a outro exemplo com *STDIN*:

```bash
$ base64
```

Este comando fica à espera de dados em uma *interface interativa* do **STDIN,** que refere-se ao teclado do computador.&#x20;

Então, devemos digitar qualquer texto (neste caso digitei "leandro") e, para finalizar, devemos pressionar a tecla *ENTER\[CR]* seguida de **CTRL+d**, que basicamente finaliza a interface STDIN. Após isto, o comando deve retornar o output:

```bash
bGVhbmRybwo=
```

* O programa `base64` fica à espera do **STDIN**, ou seja, *file descriptor 0*
* STDIN refere-se ao teclado do computador, e por isto abre-se uma *interface interativa* para receber informação a partir do teclado
* Digitei "leandro", pressionei **ENTER** e em seguida **CTRL+d** para sair da interface STDIN
* O programa capturou a informação através do *STDIN* e converteu a informação para um formato *base 64*
* O programa enviou a informação convertida em base 64 para o *STDOUT*
* STDOUT refere-se ao screen do computador, e por isto o conteúdo base 64 é mostrado na tela

Interessante notar que programas tais como o `base64` interagem tanto com o stream STDIN quanto o STDOUT.

#### STDERR

E se passarmos um argumento, por exemplo o nome de um arquivo inexistente para o comando `base64`?

```bash
$ base64 name.txt
Unable to open 'name.txt': No such file or directory
```

Como o programa fica à espera de informação a partir do STDIN, é lançado um erro caso o STDIN não proveja a informação.&#x20;

Todo programa, por padrão, envia seus erros para a stream **STDERR**, representada pelo *file descriptor 2*.&#x20;

E qual o dispositivo de saída do ***STDERR***? É o mesmo do *STDOUT*, e justamente por este motivo conseguimos ver a mensagem de erro na tela!

### Redirecionamento de streams

Recapitulando, sabemos que *file descriptors (fd)* são arquivos especiais do *filesystem*, mas para todo efeito, são **arquivos**.&#x20;

E, uma vez que standard streams são file descriptors, será possível *redirecionarmos*, ou seja, **mudarmos o destino** de um stream para outro stream ou até mesmo qualquer outro arquivo do *filesystem*?&#x20;

Sim, é possível, com o uso dos operadores `>` e `<`, utilizando o número do *fd* como prefixo.

* Redirecionamento de **STDIN**: `<`, ou `0<`
* Redirecionamento de **STDOUT**: `>`, ou `1>`
* Redirecionamento de **STDERR**: `2>`

Voltando ao exemplo do comando `echo`, podemos redirecionar o STDOUT para outro arquivo, e desta forma, o output será enviado para o arquivo de redirecionamento:

```bash
$ echo leandro 1> name.txt
```

Note que o output não foi mostrado, pois foi redirecionado para o arquivo `name.txt`. Se executarmos o comando `cat name.txt`, podemos ver o resultado no screen!

Voltando ao exemplo do base64, que tal redirecionarmos o STDIN default (dispositivo de teclas) para que seja a partir do arquivo contendo o nome?

```bash
$ base64 0< name.txt
bGVhbmRybwo=
```

Nice!&#x20;

Um "açúcar sintático" no bash é que, quando quisermos redirecionar o *STDIN* ou *STDOUT*, **não precisamos colocar o sufixo** do *file descriptor*:

```bash
$ echo leandro > name.txt # default do > é 1
$ base64 < name.txt       # default do < é 0
bGVhbmRybwo=
```

E se tentarmos com um arquivo não-existente?

```bash
$ base64 blah.txt
Unable to open 'blah.txt': No such file or directory
```

Muito similar ao erro que tivemos anteriormente. Como o arquivo **não é encontrado**, é lançado um erro, que vai para o *STDERR*, que por sua vez é mostrado por *default* no screen.

Mas podemos redirecionar o **STDERR** também para um outro arquivo, tal como o *STDOUT e STDIN*?&#x20;

Sim, mas no caso do STDERR, precisamos utilizar o sufixo do file

```bash
$ base64 blah.txt 2> err.txt
```

Confirmando que o conteúdo foi redirecionado para `err.txt`:

```bash
# Como o cat é um comando que também fica à espera de STDIN, 
#  também funciona se utilizarmos redirecionamento de STDIN
#  Exemplos:
#    cat 0< err.txt
#    cat < err.txt
# Ou, simplesmente:
$ cat err.txt
Unable to open 'blah.txt': No such file or directory
```

Podemos também concentrar tanto output quanto erros num único arquivo:

```bash
$ echo 'My message' > out.log 2>&1
```

O `&1` significa o stream atual para o file descriptor 1, neste caso o arquivo `out.log`.&#x20;

### Utilizando outros file descriptors

Com exceção dos *fd* padrão 0, 1, e 2, podemos criar outros file descriptors e utilizarmos como IPC? Sim, podemos! Vamos começar criando um diretório onde vamos armazenar estes fd temporários:

```bash
$ mkdir /tmp/fd
```

Vamos supor que queremos atribuir o `fd 42`. Devemos então preparar nosso fd, que será um canal de *comunicação de única via,* ou **single-communication channel** entre dois processos.&#x20;

Primeiro, abrimos o **fd** para para escrita `>` (STDOUT):

```bash
$ exec 42> /tmp/fd/42
```

Agora, redirecionamos uma mensagem para o *fd* utilizando **redirecionamento de stream** de escrit&#x61;**:**

```bash
$ echo 'Some message' >&42
```

Então com o comando **base64**, e sabendo que podemos ler de qualquer fd com redirecionamento de streams, fazemos o redirecionamento de *leitura:*

```bash
# Primeiro, abrir o fd para leitura < (STDIN)
$ exec 42< /tmp/fd/42 

# Executar o comando com o redirecionamento para o fd
$ base64 <&42
U29tZSBtZXNzYWdlCg==
```

File descriptors são recursos, portanto depois de utilizados, devem ser "liberados" no sistema operacional, ou seja, **fechados tanto para escrita quanto leitura**:

```bash
$ exec 42<&-
$ exec 42>&-
```

### Resumo

Nesta seção, vimos como diferentes processos se comunicam utilizando uma forma primitiva de IPC, que são os *file descriptors*.

Uma vez que entendemos isto, já podemos ir para a próxima etapa que irá explorar outra forma de comunicação um pouco mais sofisticada, **UNIX pipes**.
