Aula 14 – Estruturas de decisão ‘if’ e ‘case’
Aula 13 – O menu ‘select’ | Índice | Aula 15 – Funções
Seu apoio é muito importante para a criação e a manutenção dos cursos gratuitos do canal debxp:
14.1 – A estrutura ‘if’, ‘elif’, ‘then’, ‘else’
O comando composto if
, através de seus componentes, as palavras-chave if
e elif
, observa o status de saída de um comando. Se o comando observado terminar com status de saída 0
(sucesso), um bloco de comandos iniciado pela palavra-chave then
será executado. Caso contrário, se houver um bloco de comandos opcional iniciado pela palavra-chave else
, este é que será executado. Se não houver um bloco else
, nem outros comandos a serem observados, o comando composto if
é terminado.
A sintaxe geral é a seguinte:
if COMANDO 1
then
BLOCO DE COMANDOS 1
elif COMANDO 2
then
BLOCO DE COMANDOS 2
...
elif COMANDO N
then
BLOCO DE COMANDOS N
else
BLOCO DE COMANDOS ALTERNATIVOS
fi
Nessa estrutura, é muito importante observar que:
- Ela sempre terá, no mínimo, a palavra chave
if
, um comando observado, o bloco de comandos iniciado pelothen
e o terminador da estruturafi
; - Só pode haver uma linha
if
e ela sempre será a primeira linha da estrutura; - Todas as linhas
if
eelif
terão que ser seguidas pelos seus respectivos blocosthen
; - Os blocos
then
só são executados se os comandos observados nas linhasif
ouelif
que os antecederem terminarem com sucesso; - Só pode haver um bloco de comandos alternativos iniciado pelo
else
; - O bloco
else
só será executado se nenhuma das linhasif
ouelif
observar um comando que termine com sucesso; - O bloco
else
não pode vir sem que um blocothen
o anteceda; - O bloco
then
só é executado se as linhasif
eelif
encontrarem sucesso; - O bloco
else
só é executado se as linhasif
eelif
encontrarem erro.
Além disso, esteja atento ao fato de que uma linha de comando iniciada com um if
ou um elif
é uma linha de comando como todas as outras, no sentido de que o comando observado será executado normalmente, como se não houvesse nada antes dele. As diferenças só vão acontecer depois da execução do comando observado, que é quando o shell decide se irá ou não executar o bloco de comandos seguinte (iniciado com a palavra-chave then
).
Então, fique atento:
- O
if
não testa afirmações lógicas; - O
if
não testa comandos; - O
if
testa saídas de comandos!
14.2 – Um engano muito comum
Mas, e a estrutura abaixo?
if [[ expressão ]]
then
COMANDOS SE SUCESSO
else
COMANDOS SE ERRO
fi
Para quem não conhece o shell, parece que o teste de expressões faz parte da estrutura do if
, mas isso não é verdade. Os testes de expressões [[...]]
e [...]
, assim como a expressão aritmética ((...))
, como vimos neste curso, também são comandos. Ou seja, eles também podem ter suas saídas testadas pelo if
, mas não fazem parte da estrutura.
Aliás, de longe, estes são os comandos mais testados com o if
e o elif
, e qualquer busca em tutoriais e exemplos na internet pode comprovar isso. Mas, o maior poder do if
no Bash é a sua capacidade de testar a saída de qualquer comando.
Por exemplo:
if grep ^$USER /etc/passwd &>/dev/null; then
echo "Usuário encontrado"
else
echo "Usuário não encontrado"
fi
Aqui, nós testamos o status de saída do comando grep
, que fez a busca do nome do usuário logado no sistema ($USER
) no arquivo /etc/passwd
, que é um arquivo que armazena dados dos usuários do sistema. Normalmente, o grep
retornaria a linha encontrada no arquivo e encerraria com status 0
. Para evitar a exibição da linha do arquivo, nós redirecionamos todas as saídas (&>
) para o “limbo” do sistema, o arquivo /dev/null
.
14.3 – Operadores de encadeamento condicional
Também conhecidos (erroneamente) como "conectores lógicos" ou (mais corretamente) "conectores condicionais", os operadores &&
e ||
funcionam mais ou menos como o comando composto if
, pelo menos no que diz respeito ao fato de que ambas as construções testam saídas de comandos, mas existem diferenças importantes!
Observe este exemplo:
:~$ true && echo verdadeiro; false || echo falso
Consegue adivinhar a saída?
Isso mesmo!
verdadeiro
falso
O problema é que os conectores condicionais sempre avaliam a saída do último comando executado antes deles, ou seja, não existe a ideia de um "bloco de comandos", eles só fazem a ligação entre um comando e o comando seguinte, daí o nome: "operadores de encadeamento".
Com o comando composto if
, a coisa é diferente nesse aspecto:
:~$ if true; then echo verdadeiro; false; else echo falso; fi
verdadeiro
Enquanto não aparecer um elif
, um else
ou um fi
, tudo que vier depois do then
será executado e, em seguida, a estrutura toda será encerrada.
14.4 – A estrutura ‘case’
O comando composto case
executa seletivamente um bloco de comandos de acordo com o casamento de um padrão com o valor de uma palavra.
A sintaxe geral é a seguinte:
case PALAVRA in
PADRÃO 1) COMANDOS;;
PADRÃO 2) COMANDOS;;
...
esac
Aqui, o PADRÃO
pode ser qualquer string literal, uma lista de strings separadas por uma barra vertical (|
), que pode ser lida como “ou”, ou composto pelos mesmos símbolos utilizados para representar padrões de nomes de arquivos: [ ... ]
, ?
, e *
.
Pode haver qualquer número de blocos de padrões e comandos numa declaração case
, mas apenas o bloco que corresponder ao padrão buscado será executado se a lista de comandos correspondente a um padrão terminar com um par de ponto e vírgulas (;;
).
Existem outros delimitadores de blocos de comandos além do
;;
, mas este basta para um primeiro contato.
Também é muito comum utilizar o asterisco (*
), que casa com qualquer coisa, no último bloco de comandos para definir uma ação padrão, caso não exista correspondência com nenhum dos padrões anteriores.
Aqui está um exemplo típico de uso:
read -p "Digite o nome de uma fruta: " fruta
case ${fruta,,} in
banana|laranja|maracuj[aá]) echo "É uma fruta amarela" ;;
abacate|melancia|limão*) echo "É uma fruta verde" ;;
morango|pitanga) echo "É uma fruta vermelha" ;;
*) echo "Eu não sei a cor dessa fruta..."
exit 1
;;
esac
exit 0
No geral, como podemos ver, a ideia do comando é bem simples, mas alguns detalhes precisam ser observados.
1 – Tratamento da string que está sendo passada para o comando ‘case’
case ${fruta,,} in
Repare que aqui nós utilizamos uma expansão de parâmetros para deixar todo o valor da variável fruta
em minúsculas. Isso facilita o casamento dos padrões, já que não temos que nos preocupar com maiúsculas e minúsculas quando o usuário digitar os dados solicitados.
2 – Criando padrões que contemplam várias possibilidades de digitação
banana|laranja|maracuj[aá]) echo "É uma fruta amarela";;
Como o usuário poderia ter digitado “maracujá” sem o acento, nós utilizamos uma lista ([...]
) para informar os caracteres válidos na última posição da string. No caso, “a” ou “á”. Observe também que, no total, nós “autorizamos” a execução do bloco de comandos com o casamento de 4 strings possíveis: "banana", "laranja", "maracuja" ou "maracujá".
3 – Definindo uma opção padrão
*) echo "Eu não sei a cor dessa fruta..."
exit 1
;;
O caractere coringa *
casa com qualquer string, inclusive com strings vazias, que seria o caso do usuário teclar Enter sem digitar nada quando solicitado. Portanto, esta seria a condição padrão, caso não fosse encontrada nenhuma correspondência com as opções anteriores.
Repare também que, neste último bloco de comandos, nós incluímos o comando
exit 1
. Isso não é obrigatório, mas serve para ilustrar que é possível fazer o script retornar um status diferente do status do último comando executado, que é o retorno padrão do comando compostocase
. Comparando com os demais blocos de comando, neste último, nós também aproveitamos para mostrar que os comandos podem ser dispostos em várias linhas.