Descritores de arquivo avançados
LidoNesta aula, você vai dominar os descritores de arquivo avançados no Bash, aprendendo a abrir, ler e escrever em arquivos e sockets usando o comando `exec`, além de explorar redirecionamentos sofisticados. Com exemplos práticos e exercícios, você será capaz de gerenciar múltiplos fluxos de dados com precisão, essencial para scripts robustos e eficientes.
Os descritores de arquivo (FDs) são números que o kernel Linux usa para referenciar arquivos, pipes, sockets e outros recursos de I/O. Quando você executa um comando no shell, três descritores padrão são abertos: 0 (entrada padrão, geralmente o teclado), 1 (saída padrão, geralmente o terminal) e 2 (erro padrão). No entanto, o Bash permite que você manipule descritores adicionais (de 3 a 9) para controlar fluxos de dados com muito mais granularidade. Nesta aula, vamos explorar como abrir, ler e escrever em descritores de arquivo avançados, além de redirecionamentos que vão além do básico, preparando você para scripts complexos e eficientes.
Dominar descritores de arquivo avançados é uma habilidade crucial para administradores de sistemas e desenvolvedores que precisam lidar com logs, comunicação entre processos, leitura de configurações ou até mesmo sockets de rede. Vamos partir do uso do comando exec para abrir e fechar FDs, passando por redirecionamentos que combinam múltiplos fluxos, e finalizando com exemplos práticos que consolidam o conhecimento.
Abrindo FDs (exec)
O comando exec no Bash é extremamente versátil. Além de substituir o processo atual por um comando, ele pode ser usado para abrir ou fechar descritores de arquivo no shell atual. A sintaxe básica para abrir um FD é:
exec FD>arquivo # abre para escrita (trunca)
exec FD>>arquivo # abre para escrita (append)
exec FD<arquivo # abre para leitura
Por exemplo, para abrir o FD 3 para escrita em um arquivo de log, você usaria exec 3>log.txt. A partir desse momento, qualquer comando que redirecione para &3 escreverá nesse arquivo. É importante notar que o FD aberto permanece disponível para todo o script até que você o feche explicitamente com exec 3>&- ou exec 3<&- (para leitura). Essa persistência é fundamental para scripts que precisam manter múltiplos arquivos abertos ao mesmo tempo.
Além de arquivos regulares, você pode abrir FDs para outros recursos, como pipes nomeados ou até mesmo sockets (via /dev/tcp). Por exemplo, exec 3<>/dev/tcp/host/porta abre uma conexão TCP bidirecional. Isso permite criar clientes de rede diretamente no shell, sem ferramentas externas como nc.
Lendo e escrevendo
Depois de abrir um FD, você precisa saber como ler e escrever nele. Para escrita, o redirecionamento padrão funciona: echo "mensagem" >&3 envia a saída para o FD 3. Para leitura, você pode usar o comando read com o redirecionamento <&3 ou usar o FD diretamente em comandos que aceitam entrada. Por exemplo:
exec 3<dados.txt
read -r linha <&3
echo "Li: $linha"
exec 3<&-
Nesse exemplo, abrimos o FD 3 para leitura de dados.txt, lemos uma linha com read e depois fechamos o FD. É uma maneira eficiente de ler arquivos linha por linha sem consumir a entrada padrão. Para ler múltiplas linhas, você pode usar um loop while com read redirecionado do FD.
Também é possível ler e escrever no mesmo FD se ele foi aberto no modo bidirecional (<>). Por exemplo, para uma conexão TCP, você pode enviar uma requisição e ler a resposta no mesmo FD. Isso é útil para scripts de teste de serviços web ou APIs.
Redirecionamento avançado
O Bash oferece várias formas de redirecionamento que vão além do simples > e <. Vamos destacar algumas:
- Duplicação de FDs: Use
exec 4>&1para duplicar o FD 1 (saída padrão) no FD 4. Depois, você pode redirecionar temporariamente a saída padrão e restaurá-la comexec 1>&4. Isso é útil para capturar a saída de um bloco de comandos em uma variável. - Mover FDs:
exec 3>&1-move o FD 1 para o FD 3, fechando o original. É menos comum, mas pode ser útil em situações específicas. - Here-documents e here-strings: Embora não sejam FDs, eles permitem fornecer entrada diretamente no script, o que pode ser combinado com FDs para criar fluxos complexos.
- Redirecionamento de erro:
2>&1envia erros para a mesma saída da saída padrão. Para enviar para um FD específico, use2>&3.
Um padrão poderoso é usar FDs para capturar a saída de um comando sem afetar o fluxo principal. Por exemplo:
exec 3>&1 # salva a saída padrão no FD 3
exec 1>captura.txt # redireciona a saída padrão para um arquivo
echo "Este texto vai para captura.txt"
exec 1>&3 # restaura a saída padrão
echo "Este texto vai para o terminal"
exec 3>&- # fecha o FD 3
Exemplos
Vamos colocar em prática com um script que lê múltiplos arquivos e escreve em um log, tudo usando FDs:
#!/bin/bash
# Exemplo de uso de FDs avançados
# Abre FD 3 para escrita (log)
exec 3>>script.log
# Abre FD 4 para leitura (dados)
exec 4<dados.txt
# Lê todas as linhas do FD 4 e processa
while read -r linha <&4; do
echo "Processando: $linha" >&3
done
# Fecha os FDs
exec 3>&-
exec 4<&-
echo "Script concluído."
Outro exemplo útil é usar FDs para comunicação bidirecional com um servidor TCP:
#!/bin/bash
# Cliente HTTP simples usando /dev/tcp
exec 3<>/dev/tcp/example.com/80
echo -e "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n" >&3
# Lê a resposta
response=$(cat <&3)
echo "Resposta recebida (primeiras linhas):"
echo "$response" | head -n 10
exec 3<&-
Esses exemplos mostram como os FDs avançados podem ser aplicados em tarefas reais, desde processamento de arquivos até comunicação de rede.
Boas práticas e observações finais
- Sempre feche FDs que você abrir: Isso evita vazamento de recursos e mantém o script limpo.
- Use nomes de FDs altos (3-9) para evitar conflito com os padrões: O Bash reserva os FDs 0, 1 e 2.
- Teste se o FD foi aberto corretamente: Use
if [[ -e /proc/$$/fd/3 ]]; then ...ou verifique comexec 3<arquivo || exit 1. - Prefira FDs para leitura em vez de sub-shells quando precisar manter o estado: Sub-shells podem perder variáveis, mas FDs mantêm o contexto.
Referências
- Bash Reference Manual - Redirections
- Bash Reference Manual - Job Control (inclui FDs)
- Advanced Bash-Scripting Guide - I/O Redirection
- open(2) - Linux manual page
- dup(2) - Linux manual page
- Bash Reference Manual - Here Documents
Exercícios
Escreva um script que abra o FD 3 para escrita em um arquivo chamado "saida.txt" e escreva a saída de
ls -lanesse FD, sem afetar a saída padrão. Feche o FD ao final.✓ Resposta:#!/bin/bash exec 3>saida.txt ls -la >&3 exec 3>&- echo "Saída gravada em saida.txt"Explique a diferença entre
exec 3>arquivoeexec 3>>arquivoe mostre um exemplo de uso de cada um.✓ Resposta:exec 3>arquivoabre o arquivo para escrita, truncando (apagando) o conteúdo existente. Jáexec 3>>arquivoabre para escrita em modo append, preservando o conteúdo e adicionando ao final. Exemplo: para um log que deve preservar histórico, use append; para um arquivo temporário que pode ser sobrescrito, use truncagem.Como você faria para ler a primeira linha de um arquivo usando um FD dedicado? Escreva o código.
✓ Resposta:exec 3<meu_arquivo.txt read -r primeira_linha <&3 echo "Primeira linha: $primeira_linha" exec 3<&-Escreva um script que use um FD para capturar a saída de um comando e depois a exiba, sem usar variável. Dica: use o FD para redirecionar para um arquivo temporário e depois ler.
✓ Resposta:#!/bin/bash exec 3>temp_captura.txt date >&3 exec 3>&- exec 3<temp_captura.txt cat <&3 exec 3<&- rm temp_captura.txtQual é a utilidade de
exec 3<>/dev/tcp/host/porta? Dê um exemplo prático de uso.✓ Resposta: Isso abre uma conexão TCP bidirecional com o host na porta especificada, permitindo enviar e receber dados como se fosse um socket. Exemplo: verificar se uma porta está aberta em um servidor, enviando uma requisição e lendo a resposta, como em um teste de conectividade.