A instrução case é uma estrutura de controle condicional no Bash que permite comparar uma variável ou expressão contra uma série de padrões, executando o bloco de comandos correspondente ao primeiro padrão que coincida. É uma alternativa mais limpa e eficiente a longas cadeias de if-elif, especialmente quando se deseja testar múltiplos valores discretos. Nesta aula, exploraremos sua sintaxe, o uso de padrões glob (curingas), o comportamento de fall-through (que no Bash não ocorre por padrão) e casos de uso comuns no dia a dia do shell scripting.

Sintaxe

A sintaxe básica do case é:

case EXPRESSÃO in
    PADRÃO1)
        COMANDOS
        ;;
    PADRÃO2)
        COMANDOS
        ;;
    *)
        COMANDOS
        ;;
esac

A expressão é avaliada uma vez e comparada sequencialmente com cada padrão. Cada padrão pode ser um literal, um caractere curinga (glob) ou múltiplos padrões separados por barra vertical (|). O bloco de comandos termina com dois ponto-e-vírgulas (;;), que indicam o fim daquele caso. O padrão especial * funciona como o default de outras linguagens, capturando qualquer valor não correspondido anteriormente. A estrutura é fechada com a palavra esac (case ao contrário).

Exemplo simples:

#!/bin/bash
echo "Digite um número de 1 a 3:"
read numero
case $numero in
    1)
        echo "Você digitou um."
        ;;
    2)
        echo "Você digitou dois."
        ;;
    3)
        echo "Você digitou três."
        ;;
    *)
        echo "Número inválido."
        ;;
esac

Padrões (globs)

Os padrões no case são expressões glob, semelhantes às usadas em nomes de arquivos no shell. Eles permitem correspondências flexíveis:

  • * - corresponde a qualquer sequência de caracteres (inclusive vazia).
  • ? - corresponde a exatamente um caractere.
  • [abc] - corresponde a um dos caracteres listados (a, b ou c).
  • [a-z] - corresponde a qualquer caractere no intervalo.
  • [!abc] - corresponde a qualquer caractere exceto os listados.

Exemplo com padrões:

#!/bin/bash
echo "Digite um caractere:"
read char
case $char in
    [a-z])
        echo "Letra minúscula."
        ;;
    [A-Z])
        echo "Letra maiúscula."
        ;;
    [0-9])
        echo "Dígito."
        ;;
    *)
        echo "Outro caractere."
        ;;
esac

Múltiplos padrões podem ser combinados com | para executar o mesmo bloco para diferentes valores:

#!/bin/bash
echo "Digite sim, não ou talvez:"
read resposta
case $resposta in
    sim|s|S|Sim|SIM)
        echo "Resposta positiva."
        ;;
    não|não|n|N|Não|NÃO)
        echo "Resposta negativa."
        ;;
    talvez|t|T|Talvez|TALVEZ)
        echo "Resposta ambígua."
        ;;
    *)
        echo "Resposta desconhecida."
        ;;
esac

Fall-through

Diferentemente de linguagens como C, o case do Bash não executa fall-through automaticamente. Cada caso termina com ;;, que interrompe a execução e sai da estrutura. Se você omitir o ;;, o script continuará executando os comandos do próximo caso, mas isso geralmente é considerado um erro (a menos que intencional). Para simular fall-through, pode-se usar ;& (continue testando os próximos padrões) ou ;;& (continue executando os próximos comandos sem testar).

Exemplo com ;&:

#!/bin/bash
echo "Digite um número:"
read num
case $num in
    1)
        echo "Um"
        ;;&  # continua testando os próximos padrões
    2)
        echo "Dois"
        ;;&
    3)
        echo "Três"
        ;;&
    *)
        echo "Número: $num"
        ;;
esac

Com ;;&, todos os casos a partir do primeiro coincidente são executados:

#!/bin/bash
echo "Digite um número:"
read num
case $num in
    1)
        echo "Primeiro caso"
        ;;&  # continua executando os próximos blocos
    2)
        echo "Segundo caso"
        ;;&
    3)
        echo "Terceiro caso"
        ;;&
    *)
        echo "Último caso"
        ;;
esac

Casos de uso

O case é ideal para menus interativos, processamento de argumentos de linha de comando, tratamento de opções e validação de entrada. Exemplo de menu:

#!/bin/bash
while true; do
    echo "Escolha uma opção:"
    echo "1. Listar arquivos"
    echo "2. Mostrar data"
    echo "3. Sair"
    read opcao
    case $opcao in
        1)
            ls -l
            ;;
        2)
            date
            ;;
        3)
            echo "Saindo..."
            break
            ;;
        *)
            echo "Opção inválida."
            ;;
    esac
done

Outro uso comum é processar argumentos de script com getopts ou manualmente:

#!/bin/bash
for arg in "$@"; do
    case $arg in
        -h|--help)
            echo "Uso: $0 [-v] [-o arquivo]"
            exit 0
            ;;
        -v|--verbose)
            VERBOSE=true
            ;;
        -o|--output)
            shift
            OUTPUT=$1
            ;;
        *)
            echo "Argumento desconhecido: $arg"
            exit 1
            ;;
    esac
done

Boas práticas

  • Sempre use ;; para encerrar cada caso, a menos que intencionalmente queira fall-through.
  • Coloque o padrão *) como último para capturar entradas inesperadas.
  • Indente os comandos dentro de cada caso para legibilidade.
  • Use | para agrupar padrões que levam ao mesmo resultado.
  • Para scripts portáveis, evite ;& e ;;&, pois não são POSIX.

Referências

Exercícios

  1. Escreva um script que use case para exibir o nome do dia da semana (em português) com base em um número de 1 a 7 fornecido pelo usuário. Inclua tratamento para entradas inválidas.
  2. ✓ Resposta:
    #!/bin/bash
    echo "Digite um número de 1 a 7:"
    read num
    case $num in
        1) echo "Domingo" ;;
        2) echo "Segunda-feira" ;;
        3) echo "Terça-feira" ;;
        4) echo "Quarta-feira" ;;
        5) echo "Quinta-feira" ;;
        6) echo "Sexta-feira" ;;
        7) echo "Sábado" ;;
        *) echo "Número inválido. Digite um valor entre 1 e 7." ;;
    esac
  3. Crie um script que leia uma string e use case para determinar se ela começa com uma vogal, consoante, dígito ou outro caractere. Use padrões glob.
  4. ✓ Resposta:
    #!/bin/bash
    echo "Digite uma string:"
    read string
    primeiro=${string:0:1}
    case $primeiro in
        [aeiouAEIOU]) echo "Começa com vogal." ;;
        [bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ]) echo "Começa com consoante." ;;
        [0-9]) echo "Começa com dígito." ;;
        *) echo "Começa com outro caractere." ;;
    esac
  5. Escreva um script que simule um sistema de login simples. O usuário deve digitar um nome de usuário e, usando case, o script deve exibir uma mensagem personalizada para pelo menos três usuários diferentes (ex.: admin, guest, user) e uma mensagem padrão para outros.
  6. ✓ Resposta:
    #!/bin/bash
    echo "Digite seu nome de usuário:"
    read usuario
    case $usuario in
        admin) echo "Bem-vindo, administrador! Acesso total." ;;
        guest) echo "Bem-vindo, convidado! Acesso limitado." ;;
        user) echo "Bem-vindo, usuário! Acesso padrão." ;;
        *) echo "Usuário desconhecido. Acesso negado." ;;
    esac
  7. Crie um script que aceite um argumento de linha de comando e use case para interpretar as opções -a, -b e -c, exibindo uma mensagem para cada. Se nenhuma opção for fornecida, exiba uma mensagem de uso.
  8. ✓ Resposta:
    #!/bin/bash
    if [ $# -eq 0 ]; then
        echo "Uso: $0 [-a|-b|-c]"
        exit 1
    fi
    case $1 in
        -a) echo "Opção A selecionada." ;;
        -b) echo "Opção B selecionada." ;;
        -c) echo "Opção C selecionada." ;;
        *) echo "Opção inválida: $1. Use -a, -b ou -c." ;;
    esac
  9. Escreva um script que leia uma string e use case para verificar se ela é exatamente "sim", "não" ou "talvez" (em qualquer combinação de maiúsculas/minúsculas). Use múltiplos padrões com |.
  10. ✓ Resposta:
    #!/bin/bash
    echo "Digite sim, não ou talvez:"
    read resposta
    case $resposta in
        sim|s|S|Sim|SIM) echo "Resposta positiva." ;;
        não|não|n|N|Não|NÃO) echo "Resposta negativa." ;;
        talvez|t|T|Talvez|TALVEZ) echo "Resposta ambígua." ;;
        *) echo "Resposta não reconhecida." ;;
    esac