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.

Coringas Extendidos

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.

Entendendo o extglob

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.



Veja a relação completa dos artigos de Julio Cezar Neves