Globbing e expansão de chaves
Nesta aula, você aprenderá a fundo sobre globbing e expansão de chaves no Bash, dois recursos poderosos para lidar com arquivos e padrões de forma eficiente. Exploraremos curingas como *, ? e [], a sintaxe de chaves {}, as opções extglob e nullglob, com exemplos práticos e exercícios para consolidar o conhecimento.
O shell Bash oferece recursos poderosos para manipular nomes de arquivos e gerar strings de forma dinâmica. Entre eles, o globbing (ou expansão de caminhos) e a expansão de chaves são fundamentais para qualquer script que lide com múltiplos arquivos ou precise gerar combinações de strings. Dominar esses recursos aumenta a produtividade e evita armadilhas comuns.
Nesta aula, vamos explorar os curingas tradicionais, a sintaxe de chaves, e opções avançadas como extglob e nullglob. Ao final, você será capaz de escrever scripts mais concisos e robustos, lidando com padrões de arquivos de forma elegante.
Curingas (*, ?, [])
Os curingas são caracteres especiais que o shell expande para nomes de arquivos que correspondem a um padrão. Eles são essenciais para operações em lote, como copiar, mover ou listar arquivos que seguem um padrão específico.
Os três curingas básicos são:
*(asterisco): corresponde a qualquer sequência de caracteres (inclusive zero caracteres).?(ponto de interrogação): corresponde a exatamente um caractere.[...](colchetes): corresponde a um único caractere dentre os listados ou a um intervalo.
Vamos ver cada um em ação. Suponha que temos os seguintes arquivos em um diretório:
$ ls
dados1.txt dados2.txt dados10.txt relatorio.pdf script.sh backup.tar.gz
O curinga * é o mais comum. Por exemplo, para listar todos os arquivos com extensão .txt:
$ ls *.txt
dados1.txt dados2.txt dados10.txt
O curinga ? é útil para corresponder a um caractere exato. Por exemplo, para listar arquivos com um único caractere antes de .txt:
$ ls ?.txt
# Nenhum arquivo corresponde, pois temos dados1.txt, dados2.txt, dados10.txt
Na verdade, não há arquivo com um único caractere antes de .txt. Para encontrar arquivos com exatamente um dígito antes de .txt, podemos usar dados?.txt:
$ ls dados?.txt
dados1.txt dados2.txt
Os colchetes permitem definir um conjunto ou intervalo de caracteres. Por exemplo:
$ ls dados[12].txt
dados1.txt dados2.txt
$ ls dados[1-2].txt
dados1.txt dados2.txt
$ ls dados[!1].txt
dados2.txt dados10.txt # [!1] nega, ou seja, qualquer caractere exceto 1
É importante notar que o globbing é realizado pelo shell antes da execução do comando. Se nenhum arquivo corresponder ao padrão, o padrão é passado literalmente ao comando (a menos que nullglob esteja ativo, como veremos adiante).
Outro ponto: o globbing não atravessa diretórios por padrão. Ou seja, *.txt não corresponde a arquivos em subdiretórios. Para isso, precisaríamos de ** (com globstar ativado) ou do comando find.
Expansão de chaves ({})
A expansão de chaves é um recurso que gera strings a partir de uma lista ou intervalo. Ela é semelhante ao globbing, mas não depende de nomes de arquivos existentes: ela simplesmente expande o padrão para todas as combinações possíveis.
A sintaxe básica é {item1,item2,...} ou {início..fim} para intervalos (numéricos ou alfabéticos). Exemplos:
$ echo {a,b,c}
a b c
$ echo {1..5}
1 2 3 4 5
$ echo {a..e}
a b c d e
A expansão de chaves é frequentemente usada para criar múltiplos arquivos ou diretórios de uma só vez:
$ touch arquivo{1..3}.txt
$ ls
arquivo1.txt arquivo2.txt arquivo3.txt
Também é possível combinar listas e intervalos, e até mesmo aninhar chaves:
$ echo {1..3}{a,b}
1a 1b 2a 2b 3a 3b
$ echo {a,b{1,2}}
a b1 b2
Uma diferença crucial: a expansão de chaves ocorre antes do globbing. Ou seja, o shell primeiro expande as chaves e depois aplica os curingas aos resultados. Por exemplo:
$ ls {dados1,dados2}.txt
dados1.txt dados2.txt
Aqui, a chave é expandida para dados1 e dados2, e depois o globbing é aplicado (mas como não há curingas, não há mudança).
Outro uso comum é em loops:
for i in {1..10}; do
echo "Número $i"
done
Isso é muito mais legível do que usar seq ou listas manuais.
extglob
O Bash permite ativar padrões estendidos de globbing com a opção extglob. Quando ativada, você pode usar padrões mais complexos, semelhantes a expressões regulares, para corresponder a nomes de arquivos.
Para ativar, use shopt -s extglob. Para desativar, shopt -u extglob.
Os padrões estendidos incluem:
?(padrão)– corresponde a zero ou uma ocorrência do padrão.*(padrão)– zero ou mais ocorrências.+(padrão)– uma ou mais ocorrências.@(padrão1|padrão2)– exatamente um dos padrões.!(padrão)– qualquer coisa exceto o padrão.
Exemplos práticos:
$ touch file1.txt file2.txt file.txt backup.zip
$ shopt -s extglob
$ ls file+([0-9]).txt # arquivos com um ou mais dígitos
file1.txt file2.txt
$ ls !(*.txt) # tudo que não termina em .txt
backup.zip
$ ls @(file|backup)* # começa com file ou backup
file1.txt file2.txt file.txt backup.zip
Esses padrões são extremamente úteis para filtrar arquivos de forma precisa, evitando o uso de comandos externos como grep ou find apenas para seleção.
É importante lembrar que extglob afeta apenas o shell atual. Para torná-lo permanente, adicione shopt -s extglob ao seu ~/.bashrc.
nullglob
Por padrão, se um padrão glob não corresponder a nenhum arquivo, o Bash o deixa como está (passa o padrão literalmente ao comando). Isso pode causar comportamentos inesperados, como tentar copiar um arquivo chamado *.txt quando não há nenhum.
A opção nullglob muda esse comportamento: se nenhum arquivo corresponder, o padrão é removido (expandido para nada). Isso é útil em scripts para evitar erros.
Para ativar: shopt -s nullglob. Para desativar: shopt -u nullglob.
Exemplo:
$ shopt -s nullglob
$ ls *.txt 2>/dev/null
# Nenhum arquivo .txt, o comando ls é executado sem argumentos, listando o diretório atual
$ shopt -u nullglob
$ ls *.txt
ls: cannot access '*.txt': No such file or directory
No primeiro caso, com nullglob ativo, o padrão é removido e o ls lista o diretório atual. Sem nullglob, o comando recebe o literal *.txt e gera erro.
Em scripts, é comum usar nullglob em conjunto com arrays para armazenar listas de arquivos com segurança:
shopt -s nullglob
arquivos=(*.txt)
if (( ${#arquivos[@]} == 0 )); then
echo "Nenhum arquivo .txt encontrado."
else
echo "Encontrados ${#arquivos[@]} arquivos."
fi
Sem nullglob, o array conteria o literal *.txt se não houvesse correspondência, causando lógica incorreta.
Boas práticas e observações finais
Ao usar globbing e expansão de chaves, tenha em mente:
- Use aspas em padrões que não devem ser expandidos, como em
find . -name '*.txt'. - Combine
nullglobcom arrays para lidar com ausência de arquivos de forma robusta. - Prefira
extglobpara padrões complexos, mas documente seu uso, pois pode confundir outros desenvolvedores. - Cuidado com espaços em nomes de arquivos: sempre cite variáveis e use
"$@"para preservar argumentos.
Esses recursos são fundamentais para escrever scripts eficientes e legíveis. Pratique combinando-os para resolver tarefas do dia a dia.
Exercícios
- Liste todos os arquivos que começam com
dadose terminam com um número de 1 a 9, seguido de.txt. - Gere uma lista de todos os meses do ano usando expansão de chaves (dica: use abreviações de 3 letras, jan, fev, ...).
- Usando
extglob, liste apenas os arquivos que não terminam em.txtnem.zip. - Escreva um comando que crie os diretórios
projeto/src,projeto/docseprojeto/testsde uma só vez (usemkdir -p). - Em um script, ative
nullglobe use um array para contar quantos arquivos.logexistem no diretório atual. Se não houver nenhum, exiba uma mensagem.
ls dados[1-9].txt
echo {jan,fev,mar,abr,mai,jun,jul,ago,set,out,nov,dez}
ls !(*.txt|*.zip) (após shopt -s extglob)
mkdir -p projeto/{src,docs,tests}
shopt -s nullglob
logs=(*.log)
if (( ${#logs[@]} == 0 )); then
echo "Nenhum arquivo .log encontrado."
else
echo "Encontrados ${#logs[@]} arquivos .log"
fi