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 pelo then e o terminador da estrutura fi;
  • Só pode haver uma linha if e ela sempre será a primeira linha da estrutura;
  • Todas as linhas if e elif terão que ser seguidas pelos seus respectivos blocos then;
  • Os blocos then só são executados se os comandos observados nas linhas if ou elif que os antecederem terminarem com sucesso;
  • Só pode haver um bloco de comandos alternativos iniciado pelo else;
  • O bloco elsesó será executado se nenhuma das linhas if ou elif observar um comando que termine com sucesso;
  • O bloco else não pode vir sem que um bloco then o anteceda;
  • O bloco then só é executado se as linhas if e elif encontrarem sucesso;
  • O bloco else só é executado se as linhas if e elif 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 composto case. 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.


Aula 13 – O menu ‘select’ | Índice | Aula 15 – Funções

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Post comment