Touche fléchée / menu Entrer

11

Comment créer un menu dans un script shell qui affichera 3 options qu'un utilisateur utilisera les touches fléchées pour déplacer le curseur de surbrillance et appuyez sur Entrée pour en sélectionner une?

Mrplow911
la source
Je pense que vous n'avez pas de chance WRT pour la fonctionnalité de touches fléchées et la mise en évidence dans un script shell pur (vous pourriez être en mesure de faire ce dernier avec tput, mais je pense que le premier n'est pas possible), mais vous pouvez créer des menus simples en bash avec select: tldp.org/LDP/Bash-Beginners-Guide/html/sect_09_06.html
goldilocks
Voulez-vous dire un menu GUI (en utilisant quelque chose comme [zenity] (Ben Browder) ou un menu basé sur du texte en utilisant quelque chose comme ncurses ?
terdon
J'essaie de créer un menu qui ressemble à celui auquel vous accédez si vous devez sélectionner l'option de démarrage pour Windows ("mode sans échec" "normal", etc.)
Mrplow911
1
Il y a le dialogpaquet qui crée des interfaces de terminal de base faux GUI dans des scripts.
HalosGhost
@HalosGhost Connaissez-vous des exemples de cela?
Mrplow911

Réponses:

9

la boîte de dialogue est un excellent outil pour ce que vous essayez de réaliser. Voici l'exemple d'un menu simple à 3 choix:

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

La syntaxe est la suivante:

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

La sélection sera envoyée à stderr. Voici un exemple de script utilisant 3 couleurs.

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

Sur Debian, vous pouvez installer dialogvia le paquet du même nom .

John WH Smith
la source
22

Voici une bashsolution de script pur sous la forme de la select_optionfonction, reposant uniquement sur les séquences d'échappement ANSI et la fonction intégrée read.

Fonctionne sur Bash 4.2.45 sur OSX. Les pièces funky qui pourrait ne pas fonctionner aussi bien dans tous les environnements de tout ce que je sais les get_cursor_row(), key_input()(pour détecter touches haut / bas) et les cursor_to()fonctions.

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

Voici un exemple d'utilisation:

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 sortie ressemble à ci-dessous, avec l'option actuellement sélectionnée mise en évidence en utilisant la coloration inverse ansi (difficile à transmettre ici dans le démarque). Cela peut être adapté dans la print_selected()fonction si vous le souhaitez.

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

  [one] 
   two 
   three 

Mise à jour: Voici une petite extension select_optenveloppant la select_optionfonction ci-dessus pour la rendre facile à utiliser dans une caseinstruction:

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

Exemple d'utilisation avec 3 options littérales:

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

Vous pouvez également mélanger s'il existe des entrées connues (Oui et Non dans ce cas) et exploiter le code de sortie $?pour le cas générique:

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
la source
1
C'est beau et étonnant ; merci beaucoup pour le partage! Est-ce le vôtre à l'origine? Existe-t-il un dépôt en ligne à cloner / bifurquer? La seule chose que j'ai pu trouver qui semblait être dans le contrôle de version était sur GitHub dans steistmm 's Gist (avec la modification de ligne ajoutée) qui pointe ici, lol. Je travaille sur mes propres modifications (dans un Gist, mais je prévois de faire un dépôt) ici même si je dois encore mettre à jour avec les dernières modifications.
l3l_aze
1
Je l'ai utilisé dans un code non public. Rassemblé à partir de divers morceaux trouvés sur le web :-)
Alexander Klimetschek
Sensationnel; bon travail. J'ai commencé un dépôt avec mes modifications sur https://github.com/l3laze/sind . Jusqu'à présent, les plus grandes différences sont la gestion des entrées améliorée et l'ajout d'une barre de titre. J'espère ajouter une édition simple et multi-lignes, mais je n'ai encore rien fait pour ceux-ci au-delà de la recherche de code
l3l_aze