julho 30, 2019

Duas formas simples de criar menus no terminal com Shell Script

Uma das coisas mais importantes de um programa que dialogue com o usuário, é fornecer uma interface clara e objetiva, especialmente quando essa interação acontece na linha de comando. Por isso, nós vamos ver duas formas simples de criar menus no terminal apenas com recursos do shell.

Para efeito deste artigo, eu estou me concentrando nos menus que podem ser criados apenas com recursos e comandos do shell do Linux, sem recorrer a outros aplicativos criadores de diálogos (dialog, por exemplo) ou que serviriam para criar interfaces com visual mais gráfico no terminal (curses, ncurses, etc...)

A estrutura de um menu

Antes de vermos os métodos, eu acho interessante notar que um menu bem construído quase sempre irá obedecer a seguinte estrutura:

+------------------------------------+
| Um bloco de opções e mensagens...  |
+------------------------------------+
| Um 'prompt' para o usuário...      |
+------------------------------------+
| Algum tipo de seletor de opções... |
+------------------------------------+

No terminal, o bloco de opções geralmente é um texto estático, uma simples listagem em texto das opções que o usuário tem e os caracteres que ele deverá entrar no prompt de comando para fazer a sua escolha.

O segundo elemento nessa estrutura é o próprio prompt de comando. Geralmente é composto de uma mensagem com instruções e um cursor onde o usuário fará a sua escolha.

Finalmente, o script deverá contar com algum tipo de declaração de controle de fluxo (geralmente a declaração case) capaz de direcionar a continuação do script de acordo com as opções feitas pelo usuário.

1. O menu 'PS3 select'

Além do prompt que aparece no seu terminal (PS1), onde você digita os comandos, o Linux possui mais três:

PS2 - prompt de continuação interativa

PS3 - utilizado pelo *loop* `select` nos scripts em shell

PS4 - que dá um prefixo às saídas rastreadas por script
      (quando definimos 'set -x' no script, por exemplo)

Dos quatro, o que utilizamos para construir menus bastante simples e rápidos é o PS3 em conjunção com o loop select. Vamos ver como ficaria um script utilizando o select sem o PS3:

#!/usr/bin/env bash

clear

select opt in "opção um" \
              "opção dois" \
              "opção três" \
              "sair"
do
    case $opt in
    "opção um") echo "você escolheu a opção um"
                ;;
  "opção dois") echo "você escolheu a opção dois"
                ;;
  "opção três") echo "você escolheu a opção três"
                ;;
        "sair") echo "Ok, saindo..." 
                exit
                ;;
    esac
done


A saída do exemplo seria esta abaixo, e já seria um menu relativamente funcional, mesmo sendo bastante obscuro para um usuário menos habituado:

1) opção um
2) opção dois
3) opção três
4) sair
#? _

Repare que o prompt é apenas #?, significando que o usuário deverá entrar o número da opção desejada.

Para tornar o loop select mais amigável, basta definir o PS3 no script antes do select:

#!/usr/bin/env bash

clear

PS3="Escolha uma opção de 1 a 4: " 

select opt in "opção um" \
              "opção dois" \
              "opção três" \
              "sair"
do
    case $opt in
    "opção um") echo "você escolheu a opção um"
                ;;
  "opção dois") echo "você escolheu a opção dois"
                ;;
  "opção três") echo "você escolheu a opção três"
                ;;
        "sair") echo "Ok, saindo..." 
                exit
                ;;
    esac
done


O que resulta na saída abaixo, bem mais usável:

1) opção um
2) opção dois
3) opção três
4) sair
Escolha uma opção de 1 a 4: _

2. O menu 'while read'

Apesar de rápido e objetivo, o menu com PS3 e select é relativamente engessado em termos de opções. Se você procura um nível de comunicação maior, talvez o caminho seja utilizar comando read dentro de um loop while, como no script do exemplo abaixo:

#!/usr/bin/env bash

clear

while [[ $opt != "q" ]]; do
    echo ""
    echo "---------------------------------------------------"
    echo "Olá! Seja bem-vindo ao nosso maravilhoso menu!"
    echo "---------------------------------------------------"
    echo "1. Mostar mensagem 'você escolheu a opção um'"
    echo "2. Mostar mensagem 'você escolheu a opção dois'"
    echo "3. Mostar mensagem 'você escolheu a opção três'"
    echo "---------------------------------------------------"
    echo "q. Sair (mas não deixe de nos visitar novamente!)"
    echo "---------------------------------------------------"
    echo -n "O que você quer que eu faça? "

    read opt

    case $opt in
        1)    echo -e "\nvocê escolheu a opção um"
              ;;
        2)    echo -e "\nvocê escolheu a opção dois"
              ;;
        3)    echo -e "\nvocê escolheu a opção dois"
              ;;
        [Qq]) echo -e "\nOk, saindo... :-(\n"
              exit
              ;;
    esac

    echo -e "\ntecle 'enter' para continuar... \c"
    read continua
    clear

done

Que produziria este menu no terminal...

---------------------------------------------------
Olá! Seja bem-vindo ao nosso maravilhoso menu!
---------------------------------------------------
1. Mostar mensagem 'você escolheu a opção um'
2. Mostar mensagem 'você escolheu a opção dois'
3. Mostar mensagem 'você escolheu a opção três'
---------------------------------------------------
q. Sair (mas não deixe de nos visitar novamente!)
---------------------------------------------------
O que você quer que eu faça? _

A ideia é bastante simples: o loop while continuará sendo executado enquanto o teste da varável q continuar retornando verdadeiro. Dentro do loop, estão as mensagens exibidas e o comando read, que irá atribuir à variável opt tudo que o usuário digitar no terminal (STDIN). Finalmente, a declaração case controlará o fluxo de execução de acordo com o que o usuário escolher.

Como você pode ver, mesmo ainda sendo bastante simples, este método nos dá muito mais liberdade para trabalhar a interatividade e a usabilidade do menu.

Alguns detalhes úteis nesse tipo de menu

  • Por uma questão de maior rigor com o código, muitos programadores declaram a variável de controle (opt, no nosso caso) antes de utilizá-la no teste do loop while. Na prática, não é necessário, mas você pode declarar opt="o valor que você quiser" no começo do script se preferir.
  • Para que o cursor fique ao lado da saída do echo, nós temos duas opções: echo -n "mensagem " ou echo -e "mensagem \c".
  • Nas seleções do case, nós podemos utilizar padrões de expressões regulares, e foi isso que eu fiz quando escrevi [Qq], que é um grupo de caracteres válidos, aceitando a correspondência tanto com a entrada maiúscula quanto com a minúscula da tecla "Q".

Conclusão

Obviamente, este não é um assunto esgotado. Cada projeto tem a sua própria demanda de soluções, mas o princípio básico será sempre o mesmo. O mais importante é você estudar os exemplos que eu postei aqui e experimentar muito e tentar descobrir alternativas diferentes.

Dúvidas? Correções? Outras ideias?

Fique à vontade para comentar.

 

Nenhum comentário:

Postar um comentário

O sistema de comentários do Blogspot é um lixo e praticamente me obriga a liberar ou moderar todos os comentários. Portanto, eu peço perdão antecipadamente caso o seu comentário demore para aparecer.

Mas não se acanhe por causa disso! :-)