Tecla de flecha / menú Entrar

11

¿Cómo crear un menú en un script de shell que muestre 3 opciones que un usuario usará las teclas de flechas para mover el cursor resaltado y presione Intro para seleccionar una?

Mrplow911
fuente
Creo que no tiene suerte WRT para la funcionalidad de la tecla de flecha y resaltar en un script de shell puro (es posible que pueda hacer esto último tput, pero creo que lo primero no es posible), pero puede crear menús simples en bash con select: tldp.org/LDP/Bash-Beginners-Guide/html/sect_09_06.html
goldilocks
¿Se refiere a un menú GUI (que usa algo como [zenity] (Ben Browder) o uno basado en texto que usa algo como ncurses ?
terdon
Estoy tratando de crear un menú que sea similar al que tiene si tiene que seleccionar la opción de arranque para Windows ("modo seguro" "normal", etc.)
Mrplow911
1
Existe el dialogpaquete que crea interfaces básicas de terminal falsa-GUI en scripts.
HalosGhost
@HalosGhost ¿Conoces algún ejemplo de esto?
Mrplow911

Respuestas:

9

El diálogo es una gran herramienta para lo que está tratando de lograr. Aquí está el ejemplo de un menú simple de 3 opciones:

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

La sintaxis es la siguiente:

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

La selección se enviará a stderr. Aquí hay un script de muestra con 3 colores.

#!/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

En Debian, puede instalar a dialogtravés del paquete del mismo nombre .

John WH Smith
fuente
22

Aquí hay una bashsolución de secuencia de comandos pura en forma de select_optionfunción, que se basa únicamente en secuencias de escape ANSI y en la función integrada read.

Funciona en Bash 4.2.45 en OSX. Las partes cobardes que podrían no funcionar igual de bien en todos los ambientes de todo lo que sé son los get_cursor_row(), key_input()(para detectar arriba / abajo) y las cursor_to()funciones.

#!/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
}

Aquí hay un ejemplo 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]}"

La salida se ve a continuación, con la opción seleccionada actualmente resaltada usando coloración ansi inversa (difícil de transmitir aquí en la reducción). Esto se puede adaptar en la print_selected()función si se desea.

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

  [one] 
   two 
   three 

Actualización: Aquí hay una pequeña extensión que select_optajusta la select_optionfunción anterior para que sea fácil de usar en una casedeclaración:

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

Ejemplo de uso con 3 opciones literales:

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

También puede mezclar si hay algunas entradas conocidas (Sí y No en este caso), y aprovechar el código de salida $?para el caso comodín:

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
fuente
1
Esto es hermoso y asombroso ; muchas gracias por compartir ¿Es tuyo originalmente? ¿Hay un repositorio en línea para clonar / bifurcar? Lo único que pude encontrar que parecía estar en el control de versiones fue en GitHub en stephenmm 's Gist (con edición de línea agregada) que señala aquí, jajaja. Trabajando en mis propias modificaciones (en un Gist, pero planeando hacer un repositorio) aquí, aunque necesito actualizar con los últimos cambios todavía.
l3l_aze
1
Lo usé en algún código no público. Lo junté de varias partes encontradas en la web :-)
Alexander Klimetschek
Guau; Buen trabajo. Comencé un repositorio con mis modificaciones en https://github.com/l3laze/sind . Hasta ahora, las mayores diferencias son el manejo mejorado de entradas y la adición de una barra de título. Espero agregar edición de una o varias líneas, pero aún no he hecho nada para los que están más allá de mirar algún código
l3l_aze