Pfeiltaste / Menü eingeben

11

Wie erstelle ich ein Menü in einem Shell-Skript, das 3 Optionen anzeigt, bei denen ein Benutzer die Pfeiltasten verwendet, um den Markierungscursor zu bewegen, und die Eingabetaste drückt, um eine auszuwählen?

Mrplow911
quelle
Ich denke, Sie haben kein Glück, dass WRT die Funktionen der Pfeiltasten und das Hervorheben in einem reinen Shell-Skript unterstützt (Sie können das letztere möglicherweise tun tput, aber ich denke, das erstere ist nicht möglich), aber Sie können einfache Menüs in Bash erstellen mit select: tldp.org/LDP/Bash-Beginners-Guide/html/sect_09_06.html
Goldlöckchen
Meinen Sie ein GUI-Menü (mit etwas wie [Zenity] (Ben Browder) oder ein textbasiertes mit etwas wie ncurses ?
terdon
Ich versuche, ein Menü zu erstellen, das dem entspricht, das Sie erhalten, wenn Sie die
Startoption
1
Es gibt das dialogPaket, das grundlegende Faux-GUI-Terminalschnittstellen in Skripten erstellt.
HalosGhost
@ HalosGhost Kennen Sie Beispiele dafür?
Mrplow911

Antworten:

9

Dialog ist ein großartiges Werkzeug für das, was Sie erreichen möchten. Hier ist das Beispiel eines einfachen 3-Auswahl-Menüs:

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

Die Syntax lautet wie folgt:

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

Die Auswahl wird an gesendet stderr. Hier ist ein Beispielskript mit 3 Farben.

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

Unter Debian können Sie dialogüber das gleichnamige Paket installieren .

John WH Smith
quelle
22

Hier ist eine reine bashSkriptlösung in Form der select_optionFunktion, die sich ausschließlich auf ANSI-Escape-Sequenzen und die integrierte Funktion stützt read.

Funktioniert unter Bash 4.2.45 unter OSX. Die flippige Teile , die nicht ebenso gut von allen in allen Umgebungen funktionieren könnte ich weiß , sind die get_cursor_row(), key_input()(erkennen Tasten auf / ab) und die cursor_to()Funktionen.

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

Hier ist ein Anwendungsbeispiel:

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

Die Ausgabe sieht wie folgt aus, wobei die aktuell ausgewählte Option durch inverse Ansi-Färbung hervorgehoben wird (hier im Markdown schwer zu vermitteln). Dies kann bei Bedarf in der print_selected()Funktion angepasst werden.

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

  [one] 
   two 
   three 

Update: Hier ist eine kleine Erweiterung, select_optdie die obige select_optionFunktion umschließt, um die Verwendung in einer caseAnweisung zu vereinfachen :

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

Anwendungsbeispiel mit 3 Literaloptionen:

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

Sie können auch mischen, wenn einige Einträge bekannt sind (in diesem Fall Ja und Nein), und den Exit-Code $?für den Platzhalter verwenden:

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
quelle
1
Das ist wunderschön und erstaunlich ; Vielen Dank für das Teilen! Ist das ursprünglich dein eigenes? Gibt es ein Online-Repo zum Klonen / Gabeln? Das einzige, was ich finden konnte, das in der Versionskontrolle zu sein schien, war auf GitHub in stephenmms Gist (mit hinzugefügter Zeilenbearbeitung), das hierher verweist, lol. Ich arbeite hier an meinen eigenen Modifikationen (in einem Gist, plane aber ein Repo) , obwohl ich immer noch mit den neuesten Änderungen aktualisieren muss.
l3l_aze
1
Ich habe es in einem nicht öffentlichen Code verwendet. Zog es aus verschiedenen Teilen zusammen, die im Web gefunden wurden :-)
Alexander Klimetschek
Beeindruckend; gute Arbeit. Ich habe mit meinen Änderungen unter https://github.com/l3laze/sind ein Repo gestartet . Bisher sind die größten Unterschiede die verbesserte Eingabebehandlung und das Hinzufügen einer Titelleiste. Ich hoffe, ein- und mehrzeilige Bearbeitung hinzufügen zu können, habe aber noch nichts gegen diejenigen unternommen, die über das Betrachten von Code
hinausgehen