De acordo com as Leis 12.965/2014 e 13.709/2018, que regulam o uso da Internet e o tratamento de dados pessoais no Brasil, ao me inscrever na newsletter do portal DICAS-L, autorizo o envio de notificações por e-mail ou outros meios e declaro estar ciente e concordar com seus Termos de Uso e Política de Privacidade.
Colaboração: Julio Cezar Neves
Data de Publicação: 21 de julho de 2026
As expansões de parâmetros são ferramentas muito versáteis, já que substituem diversos comandos e muito rápidas, pois atuam como variáveis e não como comandos. Imagine você que sua versatilidade deve-se também ao fato de quando estão trabalhando com cadeias, permitirem o uso do globbing normal - que são os curingas conhecidos por todos para expansão de nomes de arquivos e diretórios (*, ?, [a-z] e [!a-z]) - e o globbing estendido, que por ser muito desconhecido, é necessário termos um bite papo sobre ele.
Quem habilita seu uso é a opção extglob, cujo padrão (default) é estar ativa. Por motivo didático, sempre vou ligar esta opção antes de usá-la, mas isso normalmente não é necessário (mas dentro de um script, é obrigatório). Por via das dúvidas, veja como saber seu estado:
$ shopt extglob # Lista estado da facilidade
extglob off # Está desligado
Está desligado, então para termos certeza que ele sempre estará ligado, podemos fazer:
$ shopt extglob | grep off && shopt -s extglob
$ shopt extglob
extglob on # Agora foi ligado
Então, conforme dá para notar por este exemplo, ligamos esta facilidade com a opção -s do comando shopt.
Nas linhas a seguir, LISTA contém um ou mais padrões separados por uma barra vertical (|), que, diga-se de passagem, é tratada como um OU lógico em expressões regulares e em diversos comandos que em sua sintaxe aceitam a formação de padrões de pesquisa usando os caracteres coringa (como o comando case, por exemplo). Veja:
?(LISTA) |
Casa zero ou uma ocorrência de LISTA (exatamente como em uma expressão regular, já que torna LISTA opcional). |
*(LISTA) |
Casa zero ou mais ocorrências de LISTA (exatamente como em uma expressão regular). |
+(LISTA) |
Casa uma ou mais ocorrências de LISTA (exatamente como em uma expressão regular). |
@(LISTA) |
Casa com exatamente uma ocorrência de LISTA. |
!(LISTA) |
Casa com qualquer coisa, exceto com LISTA (A exclamação (!) em diversos comandos (como exemplo o test) é tratada como negação). |
Para poder utilizá-los precisamos antes executar o comando o comando que acabamos de ver, que é:
$ shopt -s extglob
Veja então o nosso ambiente de trabalho que interagirá com os exemplos a seguir:
$ ls -1
app.js
app.test.js
config.json
config.production.json
dados.csv
error.log
error.log.1
error.log.2
README.md
setup.sh
1. O Operador ?(...) — Zero ou Uma ocorrência
Opcional: o padrão dentro dos parênteses pode aparecer uma vez ou nenhuma.
Se queremos listar o arquivo de configuração padrão e também a variação de produção, mas nada além disso:
$ echo config?(.production).json
config.json config.production.json
Ele casa com config.json (zero ocorrências do padrão) e com config.production.json (uma ocorrência).
2. O Operador *(...) — Zero ou Mais ocorrências
Livre: o padrão pode não aparecer, aparecer uma ou várias vezes seguidas.
Se queremos listar o arquivo de log principal e todos os seus históricos rotacionados (com números no final):
$ ls error.log?(.)*([0-9])
error.log error.log.1 error.log.2
O ponto opcional - ?(.) - foi colocado para poder casar o error.log (sem nada após - o log atual); o [0-9] busca um algarítimo. O * permite que esse algarítimo apareça zero vezes (no log atual) ou mais vezes (nos logs antigos). Também poderíamos ter feito ls error*(.[0-9]) ... O problema desta solução é que casaria também com um eventual error.log...2, já que o ponto poderia aparecer zero ou mais vezes.
3. O Operador +(...) — Uma ou Mais ocorrências
Obrigatório: o padrão precisa aparecer pelo menos uma vez.
Seguindo a mesma lógica dos logs, se quisermos listar apenas os arquivos de log antigos que já foram rotacionados (ignorando o log atual ativo):
$ echo error.log.+([0-9])
error.log.1 error.log.2
Ao usar o +, o Shell exige a presença de pelo menos um dígito após o ponto. O arquivo error.log fica de fora.
4. O Operador @(...) — Exatamente Uma das opções (OU)
Escolha exata: casa com um dos padrões separados por |.
Se o seu leitor precisa listar apenas os arquivos que são códigos ou scripts executáveis do projeto:
$ ls *.@(js|sh)
app.js app.test.js setup.sh
Ele filtra estritamente arquivos com a extensão .js OU .sh.
5. O Operador !(...) — Negação (Tudo, exceto...)
Inversão: seleciona tudo o que não se encaixa no padrão especificado.
Se o objetivo é fazer uma limpeza ou backup e listar tudo o que não é código JavaScript e nem documentação:
$ echo !(*.js|README.md)
config.json config.production.json dados.csv error.log error.log.1 error.log.2 setup.sh
O Bash remove app.js, app.test.js e README.md do resultado e exibe o restante da nossa base.
Tabela Comparativa
| Operador | Exemplo prático | O que ele traz da nossa base |
|---|---|---|
? (0 ou 1) |
config?(.production).json |
config.json, config.production.json |
* (0 ou mais) |
error.log*([0-9]) |
error.log, error.log.1, error.log.2 |
+ (1 ou mais) |
error.log.+([0-9]) |
error.log.1, error.log.2 |
@ (qualquer um deles) |
*.@(json|csv) |
config.json, config.production.json, dados.csv |
! (Negação) |
!(error*) |
app.js, app.test.js, config.json, ... (tudo que não começa com error) |
Para encerrar este assunto, já que nessa revisão só mostrei a forma em que os globs estendidos são usados na expansão de nomes de arquivos (file name expansion), que é a forma mais didática, porém menos útil. Então finalizarei esta introdução mostrando mais dois exemplos: um usando-as com o comando test e outro com uma Expansão de Parâmetros básica dentro de um comando test.
Primeiramente vamos usar o comando test em conjunto com os coringas estendidos que acabamos de falar. Então para saber se uma variável é um número inteiro ou real (decimal) com vírgulas ou pontos decimais, podemos fazer:
$ [[ $Var == +([[:digit:],.]) ]] && > echo É numérico || > echo Não é numérico
Onde o +([[:digit;],.]) obriga que tenha pelo menos um desses caracteres e seja formado somente por eles.
Veja qual é o problema dessa construção:
$ Var=,
$ [[ $Var == +([[:digit:],.]) ]] &&
> echo É numérico ||
> echo Não é numérico
É numérico
Ainda usando somente coringas, poderíamos melhorá-la fazendo:
$ [[ $Var == +([0-9])*([0-9,.]) ]] &&
> echo É numérico ||
> echo Não é numérico
Não é numérico
Uma forma interessante de checarmos se a resposta a uma pergunta foi s, S, sim, Sim, y, Y, yes, Yes, YES, ... é fazermos:
[[ ${Resp^^} == @(S|SIM|Y|YES) ]] &&
echo Resposta positiva
Onde:
${Resp^^}, como já vimos, é uma Expansão de Parâmetros que passa para maiúsculas o conteúdo da variável $Resp.
O comando:
[[ ${Resp^^} == @(S?(IM)|Y?(ES)) ]] &&
echo Resposta positiva
Faria o mesmo, mas esta expressão casaria com S ou (|) Y, colocando IM e ES como opcionais (?) respectivamente. O Shell é muito versátil para quem o domina.