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

STDOUT

Vamos analisar o seguinte comando:

$ 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:

$ base64

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

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:

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?

$ 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.

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

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.

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?

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:

$ 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?

$ base64 0< name.txt
bGVhbmRybwo=

Nice!

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

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

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

$ 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?

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

$ base64 blah.txt 2> err.txt

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

# 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:

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

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

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:

$ 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.

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

$ exec 42> /tmp/fd/42

Agora, redirecionamos uma mensagem para o fd utilizando redirecionamento de stream de escrita:

$ 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:

# 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:

$ 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.

Last updated