Estratégias Poderosas de Looping: Iterando Arquivos e Listas em Scripts Bash
Domine técnicas essenciais de looping em Bash usando `for` e `while` para automatizar tarefas repetitivas do sistema de forma eficiente. Este guia abrangente aborda iteração sobre listas, processamento de sequências numéricas e manipulação robusta de arquivos linha por linha usando as melhores práticas como `while IFS= read -r`. Aprenda a sintaxe fundamental, controle avançado de loops (`break`, `continue`) e técnicas essenciais para scripts de shell poderosos e confiáveis, completos com exemplos práticos de código.
Estratégias Poderosas de Looping: Iterando Arquivos e Listas em Scripts Bash
Os loops Bash são onde pequenos comandos de shell se tornam automação útil. Seja para processar cada arquivo em um diretório, executar uma tarefa um número definido de vezes ou ler dados de configuração linha por linha, os loops fornecem a estrutura para repetir o trabalho sem copiar e colar comandos.
Os dois loops que você mais usará são for e while. Use for quando você já tem um conjunto conhecido de itens, como um array ou um glob de arquivos. Use while quando o loop é conduzido por uma condição ou pela leitura de entrada. Essa divisão simples mantém muitos scripts mais fáceis de raciocinar.
O Loop for: Iterando sobre Conjuntos Fixos
O loop for é ideal quando você conhece a coleção de itens que precisa processar com antecedência. Essa coleção pode ser uma lista explícita de valores, os resultados de um comando ou um conjunto de arquivos encontrados via globbing.
1. Iterando sobre Listas Padrão
O caso de uso mais simples é iterar sobre uma lista curta de palavras escritas diretamente no script.
Sintaxe
for VARIABLE in LIST_OF_ITEMS; do
# Comandos usando $VARIABLE
done
Exemplo: Processando uma Lista de Usuários
# Lista de usuários para processar
USERS="alice bob charlie"
for user in $USERS; do
echo "Verificando diretório home para $user..."
if [ -d "/home/$user" ]; then
echo "$user está ativo."
else
echo "Aviso: Diretório home de $user não encontrado."
fi
done
Esse padrão é adequado para nomes simples. Se um item pode conter espaços, use um array em vez de uma string separada por espaços:
USERS=("alice" "bob" "mary jane")
for user in "${USERS[@]}"; do
echo "Verificando $user"
done
2. Iteração Numérica no Estilo C
Para tarefas que exigem contagem ou sequências numéricas específicas, o Bash suporta um loop for no estilo C, frequentemente combinado com expansão de chaves ou o comando seq.
Sintaxe (Estilo C)
for (( INITIALIZATION; CONDITION; INCREMENT )); do
# Comandos
done
Exemplo: Script de Contagem Regressiva
# Loop 5 vezes (i começa em 1, continua enquanto i for menor ou igual a 5)
for (( i=1; i<=5; i++ )); do
echo "Número da iteração: $i"
sleep 1
done
echo "Concluído!"
Alternativa: Usando Expansão de Chaves para Sequências Simples
A expansão de chaves é mais simples e rápida do que usar seq para gerar inteiros contíguos ou sequências.
# Gera números de 10 a 1
for num in {10..1}; do
echo "Contagem regressiva: $num"
done
3. Iterando sobre Arquivos e Diretórios (Globbing)
Usar curingas (*) dentro do loop for permite processar arquivos que correspondem a um padrão específico, como todos os arquivos de log ou todos os scripts em um diretório.
Exemplo: Arquivando Arquivos de Log
Coloque a variável entre aspas ("$file") ao lidar com nomes de arquivos, especialmente aqueles que contêm espaços ou caracteres especiais.
TARGET_DIR="/var/log/application"
# Loop sobre todos os arquivos que terminam em .log no diretório de destino
for logfile in "$TARGET_DIR"/*.log; do
# Verifica se o arquivo realmente existe (evita executar no literal "*.log" se nenhum arquivo corresponder)
if [ -f "$logfile" ]; then
echo "Comprimindo $logfile..."
gzip "$logfile"
fi
done
O Loop while: Execução Baseada em Condição
O loop while continua executando um bloco de comandos enquanto uma condição especificada permanecer verdadeira. É comumente usado para ler fluxos de entrada, monitorar condições ou lidar com tarefas onde o número de iterações é desconhecido.
1. Loop while Básico
Sintaxe
while CONDITION; do
# Comandos
done
Exemplo: Aguardando um Recurso
Este loop usa o comando test ([ ]) para verificar se um diretório existe antes de prosseguir.
RESOURCE_PATH="/mnt/data/share"
while [ ! -d "$RESOURCE_PATH" ]; do
echo "Aguardando o recurso $RESOURCE_PATH ser montado..."
sleep 5
done
echo "Recurso disponível. Iniciando backup."
2. O Padrão Robusto while read
A aplicação mais poderosa do loop while é ler o conteúdo de um arquivo ou fluxo de saída linha por linha. Este padrão é muito superior a usar um loop for na saída de cat, pois lida de forma confiável com espaços e caracteres especiais.
Melhor Prática: Lendo Linha por Linha
Para garantir a máxima robustez, utilizamos três componentes principais:
IFS=: Limpa o Separador de Campos Interno, garantindo que a linha inteira, incluindo espaços iniciais/finais, seja lida na variável.read -r: A opção-revita a interpretação de barras invertidas (leitura bruta), o que é crítico para caminhos e strings complexas.- Redirecionamento de Entrada (
<): Redireciona o conteúdo do arquivo para dentro do loop, garantindo que o loop seja executado no contexto do shell atual (evitando problemas de subshell).
# Arquivo contendo dados, um item por linha
CONFIG_FILE="/etc/app/servers.txt"
while IFS= read -r server_name; do
# Pular linhas vazias ou comentadas
if [[ -z "$server_name" || "$server_name" =~ ^# ]]; then
continue
fi
echo "Pingando servidor: $server_name"
ping -c 1 "$server_name"
done < "$CONFIG_FILE"
Dica: Evitando
catem LoopsPrefira
while ... done < fileem vez decat file | while ...ao ler um arquivo. Na maioria das configurações do Bash, um pipeline executa o loop em um subshell, então as variáveis alteradas dentro do loop são perdidas quando o loop termina.
3. Lidando com Nomes de Arquivos do find
Para processamento recursivo de arquivos, evite analisar a saída simples do find linha por linha. Nomes de arquivos podem conter espaços e, raramente, novas linhas. Use saída delimitada por nulo:
find /var/log/application -type f -name '*.log' -print0 |
while IFS= read -r -d '' logfile; do
echo "Log encontrado: $logfile"
gzip -- "$logfile"
done
O par -print0 e read -d '' trata o byte nulo como separador. O -- antes de "$logfile" informa ao gzip que os valores seguintes são operandos, não opções, o que protege você de nomes de arquivos que começam com -.
Controle Avançado de Loops e Técnicas
Scripts eficazes exigem a capacidade de controlar a execução do loop com base em condições de tempo de execução.
1. Controlando o Fluxo: break e continue
break: Sai imediatamente do loop inteiro, independentemente das iterações ou condições restantes.continue: Pula a iteração atual e salta imediatamente para a próxima iteração (ou reavalia a condiçãowhile).
Exemplo: Pesquisar e Parar
SEARCH_TARGET="target.conf"
for file in /etc/*; do
if [ -f "$file" ] && [[ "$file" == *"$SEARCH_TARGET"* ]]; then
echo "Configuração alvo encontrada em: $file"
break # Parar o processamento uma vez encontrado
elif [ -d "$file" ]; then
continue # Pular diretórios, verificar apenas arquivos
fi
echo "Verificando arquivo: $file"
done
2. Lidando com Delimitadores Complexos usando IFS
Enquanto a leitura de arquivos linha por linha requer a limpeza de IFS, iterar sobre uma lista separada por um caractere diferente (como uma vírgula) requer a definição temporária de IFS.
CSV_DATA="data1,data2,data3,data4"
OLD_IFS=$IFS # Salvar o IFS original
IFS=',' # Definir IFS para o caractere vírgula
for item in $CSV_DATA; do
echo "Item encontrado: $item"
done
IFS=$OLD_IFS # Restaurar o IFS original imediatamente após o loop
Aviso: Alterações Globais em
IFSSempre salve o
$IFSoriginal antes de modificá-lo dentro de um script (por exemplo,OLD_IFS=$IFS). A falha em restaurar o valor original pode causar comportamento imprevisível em comandos subsequentes.
Melhores Práticas para Loops Bash Robusto
| Prática | Justificativa |
|---|---|
| Sempre Coloque Variáveis entre Aspas | Use "$variable" para evitar divisão de palavras e expansão de glob, especialmente na iteração de arquivos. |
Use while IFS= read -r |
O método mais confiável para processar arquivos linha por linha, lidando corretamente com espaços e caracteres especiais. |
| Verifique a Existência | Ao usar globbing (*.txt), sempre inclua uma verificação (if [ -f "$file" ];) para garantir que o loop não processe o nome do padrão literal se nenhum arquivo corresponder. |
| Localize Variáveis | Use a palavra-chave local dentro de funções para evitar que variáveis de loop sobrescrevam acidentalmente variáveis globais. |
| Use Comandos Internos em vez de Comandos Externos | Use expansão de chaves ({1..10}) ou loops estilo C em vez de gerar comandos externos como seq para desempenho. |
Uma Regra Prática
Use arrays para listas na memória, globs para conjuntos simples de arquivos, while IFS= read -r para entrada orientada a linhas e saída find delimitada por nulo para manipulação recursiva de nomes de arquivos. Coloque expansões entre aspas por padrão. Adicione verificações de existência em torno de globs. Mantenha break e continue para casos em que eles tornam o loop mais fácil de ler, não como uma forma de esconder fluxo de controle complicado.
A maioria dos bugs de loop Bash vem da divisão de palavras, nomes de arquivos inesperados ou assumir que a entrada é mais limpa do que realmente é. Se o seu loop lida com espaços, linhas vazias, comentários e correspondências ausentes deliberadamente, ele sobreviverá ao trabalho de automação real.