Tecla de seta / Entrar no menu

11

Como criar um menu em um script de shell que exibirá 3 opções em que o usuário usará as teclas de setas para mover o cursor de destaque e pressione enter para selecionar uma?

Mrplow911
fonte
Eu acho que você está sem sorte no WRT com a funcionalidade das teclas de seta e o destaque em um script shell puro (você pode fazer o último com tput, mas acho que o primeiro não é possível), mas você pode criar menus simples no bash com select: tldp.org/LDP/Bash-Beginners-Guide/html/sect_09_06.html
goldilocks
Você quer dizer um menu GUI (usando algo como [zenity] (Ben Browder) ou um usando baseado em texto algo como ncurses ?
terdon
Eu estou tentando criar um menu que é como o que você chegar a se você teve para selecionar a opção de inicialização para Windows ( "modo de segurança" "normal" etc)
Mrplow911
1
Existe o dialogpacote que cria interfaces básicas de terminal faux-GUI em scripts.
HalosGhost
@HalosGhost Você conhece algum exemplo disso?
Mrplow911

Respostas:

9

O diálogo é uma ótima ferramenta para o que você está tentando alcançar. Aqui está o exemplo de um menu simples de 3 opções:

dialog --menu "Choose one:" 10 30 3 \
    1 Red \
    2 Green \
    3 Blue

A sintaxe é a seguinte:

dialog --menu <text> <height> <width> <menu-height> [<tag><item>]

A seleção será enviada para stderr. Aqui está um exemplo de script usando 3 cores.

#!/bin/bash
TMPFILE=$(mktemp)

dialog --menu "Choose one:" 10 30 3 \
    1 Red \
    2 Green \
    3 Blue 2>$TMPFILE

RESULT=$(cat $TMPFILE)

case $RESULT in
    1) echo "Red";;
    2) echo "Green";;
    3) echo "Blue";;
    *) echo "Unknown color";;
esac

rm $TMPFILE

No Debian, você pode instalar dialogatravés do pacote com o mesmo nome .

John WH Smith
fonte
22

Aqui está uma bashsolução de script puro na forma da select_optionfunção, contando apenas com as seqüências de escape ANSI e o built-in read.

Funciona no Bash 4.2.45 no OSX. As partes descoladas que podem não funcionar igualmente bem em todos os ambientes, pelo que sei são get_cursor_row(): key_input()(para detectar teclas para cima / para baixo) e as cursor_to()funções.

#!/usr/bin/env bash

# Renders a text based list of options that can be selected by the
# user using up, down and enter keys and returns the chosen option.
#
#   Arguments   : list of options, maximum of 256
#                 "opt1" "opt2" ...
#   Return value: selected index (0 for opt1, 1 for opt2 ...)
function select_option {

    # little helpers for terminal print control and key input
    ESC=$( printf "\033")
    cursor_blink_on()  { printf "$ESC[?25h"; }
    cursor_blink_off() { printf "$ESC[?25l"; }
    cursor_to()        { printf "$ESC[$1;${2:-1}H"; }
    print_option()     { printf "   $1 "; }
    print_selected()   { printf "  $ESC[7m $1 $ESC[27m"; }
    get_cursor_row()   { IFS=';' read -sdR -p $'\E[6n' ROW COL; echo ${ROW#*[}; }
    key_input()        { read -s -n3 key 2>/dev/null >&2
                         if [[ $key = $ESC[A ]]; then echo up;    fi
                         if [[ $key = $ESC[B ]]; then echo down;  fi
                         if [[ $key = ""     ]]; then echo enter; fi; }

    # initially print empty new lines (scroll down if at bottom of screen)
    for opt; do printf "\n"; done

    # determine current screen position for overwriting the options
    local lastrow=`get_cursor_row`
    local startrow=$(($lastrow - $#))

    # ensure cursor and input echoing back on upon a ctrl+c during read -s
    trap "cursor_blink_on; stty echo; printf '\n'; exit" 2
    cursor_blink_off

    local selected=0
    while true; do
        # print options by overwriting the last lines
        local idx=0
        for opt; do
            cursor_to $(($startrow + $idx))
            if [ $idx -eq $selected ]; then
                print_selected "$opt"
            else
                print_option "$opt"
            fi
            ((idx++))
        done

        # user key control
        case `key_input` in
            enter) break;;
            up)    ((selected--));
                   if [ $selected -lt 0 ]; then selected=$(($# - 1)); fi;;
            down)  ((selected++));
                   if [ $selected -ge $# ]; then selected=0; fi;;
        esac
    done

    # cursor position back to normal
    cursor_to $lastrow
    printf "\n"
    cursor_blink_on

    return $selected
}

Aqui está um exemplo de uso:

echo "Select one option using up/down keys and enter to confirm:"
echo

options=("one" "two" "three")

select_option "${options[@]}"
choice=$?

echo "Choosen index = $choice"
echo "        value = ${options[$choice]}"

A saída é semelhante a abaixo, com a opção atualmente selecionada realçada usando a coloração ansi inversa (difícil de transmitir aqui na marcação). Isso pode ser adaptado na print_selected()função, se desejado.

Select one option using up/down keys and enter to confirm:

  [one] 
   two 
   three 

Atualização: Aqui está uma pequena extensão que select_optenvolve a select_optionfunção acima para facilitar o uso em uma caseinstrução:

function select_opt {
    select_option "$@" 1>&2
    local result=$?
    echo $result
    return $result
}

Exemplo de uso com 3 opções literais:

case `select_opt "Yes" "No" "Cancel"` in
    0) echo "selected Yes";;
    1) echo "selected No";;
    2) echo "selected Cancel";;
esac

Você também pode misturar se houver algumas entradas conhecidas (Sim e Não neste caso) e aproveitar o código de saída $?do caso curinga:

options=("Yes" "No" "${array[@]}") # join arrays to add some variable array
case `select_opt "${options[@]}"` in
    0) echo "selected Yes";;
    1) echo "selected No";;
    *) echo "selected ${options[$?]}";;
esac
Alexander Klimetschek
fonte
1
Isso é lindo e incrível ; muito obrigado por compartilhar! Isso é originalmente seu? Existe um repositório online para clonar / bifurcar? A única coisa que pude achar que parecia estar no controle de versão foi no GitHub no Gist do stephenmm (com edição de linha adicionada), que aponta para aqui, lol. Trabalhando em minhas próprias modificações (em um Gist, mas planejando fazer um repo) aqui, embora eu precise atualizar as alterações mais recentes ainda.
L3l_aze # 14/18
1
Eu usei em algum código não público. Puxou-o junto de vários pedaços e fragmentos encontrados na web :-)
Alexander Klimetschek
Uau; bom trabalho. Iniciei um repositório com minhas modificações em https://github.com/l3laze/sind . Até agora, as maiores diferenças são o manuseio de entrada atualizado e a adição de uma barra de título. Espero adicionar edição única e com várias linhas, mas ainda não fiz nada para além de olhar para algum código
l3l_aze