Tasto freccia / Entra nel menu


11

Come creare un menu in uno script shell che visualizzerà 3 opzioni che un utente utilizzerà i tasti freccia per spostare il cursore di evidenziazione e premere Invio per selezionarne uno?


Penso che tu sia sfortunato WRT per la funzionalità del tasto freccia e l'evidenziazione in uno script di shell pura (potresti essere in grado di fare quest'ultimo con tput, ma penso che il primo non sia possibile), ma puoi creare semplici menu in bash con select: tldp.org/LDP/Bash-Beginners-Guide/html/sect_09_06.html
goldilocks

Intendi un menu della GUI (usando qualcosa come [zenity] (Ben Browder) o uno basato sul testo usando qualcosa come ncurses ?
terdon

Sto cercando di creare un menu simile a quello che si ottiene se si dovesse selezionare l'opzione di avvio per Windows ("modalità provvisoria" "normale" ecc.)
Mrplow911

1
Esiste il dialogpacchetto che crea interfacce terminali di base in finta GUI negli script.
HalosGhost,

@HalosGhost Conosci qualche esempio di questo?
Mrplow911,

Risposte:


9

il dialogo è un ottimo strumento per ciò che stai cercando di ottenere. Ecco l'esempio di un semplice menu a 3 scelte:

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

La sintassi è la seguente:

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

La selezione verrà inviata a stderr. Ecco uno script di esempio che utilizza 3 colori.

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

Su Debian, puoi installarlo dialogattraverso il pacchetto con lo stesso nome .


22

Ecco una bashsoluzione di script pura in forma di select_optionfunzione, basandosi esclusivamente su sequenze di escape ANSI e built-in read.

Funziona su Bash 4.2.45 su OSX. Le parti funky che potrebbero non funzionare altrettanto bene in tutti gli ambienti da tutto quello che so sono i get_cursor_row(), key_input()(per rilevare su / giù) e le cursor_to()funzioni.

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

Ecco un esempio di utilizzo:

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]}"

L'output appare come sotto, con l'opzione attualmente selezionata evidenziata usando la colorazione ansi inversa (difficile da comunicare qui in markdown). Questo può essere adattato nella print_selected()funzione se lo si desidera.

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

  [one] 
   two 
   three 

Aggiornamento: ecco una piccola estensione che select_optavvolge la select_optionfunzione sopra per facilitarne l'uso in caseun'istruzione:

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

Esempio di utilizzo con 3 opzioni letterali:

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

Puoi anche mescolare se ci sono alcune voci conosciute (Sì e No in questo caso) e sfruttare il codice di uscita $?per il caso jolly:

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

1
Questo è bellissimo e sorprendente ; grazie mille per la condivisione! È originariamente tuo? Esiste un repository online per clonare / fork? L'unica cosa che ho trovato che sembrava essere nel controllo della versione era su GitHub in Stephenmm 's Gist (con l'aggiunta del line editing) che punta a qui, lol. Sto lavorando alle mie modifiche (in un Gist, ma sto pianificando di fare un repository) qui, anche se devo aggiornare con le ultime modifiche ancora.
l3l_aze

1
L'ho usato in un codice non pubblico. L'ho messo insieme da vari pezzi trovati sul web :-)
Alexander Klimetschek

Wow; bel lavoro. Ho avviato un repository con le mie modifiche su https://github.com/l3laze/sind . Finora le maggiori differenze sono la gestione degli input aggiornata e l'aggiunta di una barra del titolo. Spero di aggiungere l'editing a linea singola e multilinea, ma non ho fatto nulla per quelli ancora oltre a guardare un po 'di codice
l3l_aze
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.