it-swarm.com.de

Mehrfachauswahlmenü im Bash-Skript

Ich bin ein Bash-Neuling, möchte aber ein Skript erstellen, in dem der Benutzer mehrere Optionen aus einer Liste von Optionen auswählen kann.

Im Wesentlichen möchte ich etwas Ähnliches wie das folgende Beispiel:

       #!/bin/bash
       OPTIONS="Hello Quit"
       select opt in $OPTIONS; do
           if [ "$opt" = "Quit" ]; then
            echo done
            exit
           Elif [ "$opt" = "Hello" ]; then
            echo Hello World
           else
            clear
            echo bad option
           fi
       done

(Quelle: http://www.faqs.org/docs/Linux-HOWTO/Bash-Prog-Intro-HOWTO.html#ss9.1 )

Mein Skript hätte jedoch mehr Optionen, und ich möchte die Auswahl von Vielfachen zulassen. Also so etwas wie das:

1) Option 1
2) Option 2
3) Option 3
4) Option 4
5) Fertig

Ein Feedback zu den ausgewählten Personen wäre ebenfalls großartig, z. B. Pluszeichen neben den bereits ausgewählten. Wenn Sie beispielsweise "1" auswählen, möchte ich eine Seite zum Löschen und erneuten Drucken erstellen:

1) Option 1 +
2) Option 2
3) Option 3
4) Option 4
5) Done

Wenn Sie dann "3" auswählen:

1) Option 1 +
2) Option 2
3) Option 3 +
4) Option 4
5) Done

Wenn sie erneut (1) auswählen, möchte ich, dass die Option "abgewählt" wird:

1) Option 1
2) Option 2
3) Option 3 +
4) Option 4
5) Done

Und schließlich, wenn Fertig gedrückt wird, möchte ich, dass eine Liste derjenigen, die ausgewählt wurden, angezeigt wird, bevor das Programm beendet wird, z. B. wenn der aktuelle Status lautet:

1) Option 1
2) Option 2 +
3) Option 3 + 
4) Option 4 +
5) Done

Durch Drücken von 5 sollte Folgendes gedruckt werden:

Option 2, Option 3, Option 4

... und das Skript wird beendet.

Meine Frage: Ist dies in Bash möglich, und wenn ja, kann jemand ein Codebeispiel bereitstellen?

Jeder Rat wäre sehr dankbar.

31
user38939

Ich denke, Sie sollten sich Dialog oder Whiptail ansehen.

(dialog box

Bearbeiten:

Hier ist ein Beispielskript mit den Optionen aus Ihrer Frage:

#!/bin/bash
cmd=(dialog --separate-output --checklist "Select options:" 22 76 16)
options=(1 "Option 1" off    # any option can be set to default to "on"
         2 "Option 2" off
         3 "Option 3" off
         4 "Option 4" off)
choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty)
clear
for choice in $choices
do
    case $choice in
        1)
            echo "First Option"
            ;;
        2)
            echo "Second Option"
            ;;
        3)
            echo "Third Option"
            ;;
        4)
            echo "Fourth Option"
            ;;
    esac
done

Wenn Sie denken, dass whiptail komplex ist, geht es hier um einen Nur-Bash-Code, der gena das tut, was Sie wollen. Es ist kurz (~ 20 Zeilen), aber für Anfänger etwas kryptisch. Neben "+" für aktivierte Optionen wird auch Feedback für jede Benutzeraktion angezeigt ("ungültige Option", "Option X wurde aktiviert"/deaktiviert usw.).

Das heißt, los geht's!

Hoffe es gefällt euch ... es war eine ziemlich lustige Herausforderung, es zu schaffen :)

#!/bin/bash

# customize with your own.
options=("AAA" "BBB" "CCC" "DDD")

menu() {
    echo "Avaliable options:"
    for i in ${!options[@]}; do 
        printf "%3d%s) %s\n" $((i+1)) "${choices[i]:- }" "${options[i]}"
    done
    if [[ "$msg" ]]; then echo "$msg"; fi
}

Prompt="Check an option (again to uncheck, ENTER when done): "
while menu && read -rp "$Prompt" num && [[ "$num" ]]; do
    [[ "$num" != *[![:digit:]]* ]] &&
    (( num > 0 && num <= ${#options[@]} )) ||
    { msg="Invalid option: $num"; continue; }
    ((num--)); msg="${options[num]} was ${choices[num]:+un}checked"
    [[ "${choices[num]}" ]] && choices[num]="" || choices[num]="+"
done

printf "You selected"; msg=" nothing"
for i in ${!options[@]}; do 
    [[ "${choices[i]}" ]] && { printf " %s" "${options[i]}"; msg=""; }
done
echo "$msg"
28
MestreLion

Hier ist eine Möglichkeit, genau das zu tun, was Sie möchten, indem Sie nur Bash-Funktionen ohne externe Abhängigkeiten verwenden. Es markiert die aktuelle Auswahl und ermöglicht das Umschalten.

#!/bin/bash
# Purpose: Demonstrate usage of select and case with toggleable flags to indicate choices
# 2013-05-10 - Dennis Williamson

choice () {
    local choice=$1
    if [[ ${opts[choice]} ]] # toggle
    then
        opts[choice]=
    else
        opts[choice]=+
    fi
}

PS3='Please enter your choice: '
while :
do
    clear
    options=("Option 1 ${opts[1]}" "Option 2 ${opts[2]}" "Option 3 ${opts[3]}" "Done")
    select opt in "${options[@]}"
    do
        case $opt in
            "Option 1 ${opts[1]}")
                choice 1
                break
                ;;
            "Option 2 ${opts[2]}")
                choice 2
                break
                ;;
            "Option 3 ${opts[3]}")
                choice 3
                break
                ;;
            "Option 4 ${opts[4]}")
                choice 4
                break
                ;;
            "Done")
                break 2
                ;;
            *) printf '%s\n' 'invalid option';;
        esac
    done
done

printf '%s\n' 'Options chosen:'
for opt in "${!opts[@]}"
do
    if [[ ${opts[opt]} ]]
    then
        printf '%s\n' "Option $opt"
    fi
done

Ändern Sie für ksh die ersten beiden Zeilen der Funktion:

function choice {
    typeset choice=$1

und der Shebang zu #!/bin/ksh.

Ich habe eine Bibliothek namens Fragebogen geschrieben, eine Mini-DSL zum Erstellen von Befehlszeilen-Fragebögen. Der Benutzer wird aufgefordert, eine Reihe von Fragen zu beantworten, und die Antworten werden an stdout gedruckt.

Das macht Ihre Aufgabe wirklich einfach. Installieren Sie es mit pip install questionnaire und erstellen Sie ein Skript, z. questions.py, so was:

from questionnaire import Questionnaire
q = Questionnaire(out_type='plain')

q.add_question('options', Prompt='Choose some options', prompter='multiple',
               options=['Option 1', 'Option 2', 'Option 3', 'Option 4'], all=None)

q.run()

Dann renne python questions.py. Wenn Sie mit der Beantwortung der Fragen fertig sind, werden sie auf Standard gedruckt. Es funktioniert mit Python 2 und 3, von denen eine mit ziemlicher Sicherheit auf Ihrem System installiert ist.

Es kann auch viel kompliziertere Fragebögen verarbeiten, falls jemand dies tun möchte. Hier sind einige Funktionen:

  • Druckt Antworten als JSON (oder als einfacher Text) an stdout
  • Ermöglicht Benutzern, zurück zu gehen und Fragen erneut zu beantworten
  • Unterstützt bedingte Fragen (Fragen können von vorherigen Antworten abhängen)
  • Unterstützt die folgenden Arten von Fragen: Roheingabe, wählen Sie eine, wählen Sie viele
  • Keine obligatorische Kopplung zwischen Fragenpräsentation und Antwortwerten
2
kylebebak

Hier ist eine Bash-Funktion, mit der der Benutzer mehrere Optionen mit den Pfeiltasten und der Leertaste auswählen und mit der Eingabetaste bestätigen kann. Es hat eine schöne menüähnliche Atmosphäre. Ich habe es mit Hilfe von https://unix.stackexchange.com/a/415155 geschrieben. Es kann so genannt werden:

multiselect result "Option 1;Option 2;Option 3" "true;;true"

Das Ergebnis wird als Array in einer Variablen gespeichert, wobei der Name als erstes Argument angegeben wird. Das letzte Argument ist optional und wird verwendet, um einige Optionen standardmäßig auszuwählen. Es sieht so aus.

function Prompt_for_multiselect {

    # 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_inactive()    { printf "$2   $1 "; }
    print_active()      { printf "$2  $ESC[7m $1 $ESC[27m"; }
    get_cursor_row()    { IFS=';' read -sdR -p $'\E[6n' ROW COL; echo ${ROW#*[}; }
    key_input()         {
      local key
      IFS= read -rsn1 key 2>/dev/null >&2
      if [[ $key = ""      ]]; then echo enter; fi;
      if [[ $key = $'\x20' ]]; then echo space; fi;
      if [[ $key = $'\x1b' ]]; then
        read -rsn2 key
        if [[ $key = [A ]]; then echo up;    fi;
        if [[ $key = [B ]]; then echo down;  fi;
      fi 
    }
    toggle_option()    {
      local arr_name=$1
      eval "local arr=(\"\${${arr_name}[@]}\")"
      local option=$2
      if [[ ${arr[option]} == true ]]; then
        arr[option]=
      else
        arr[option]=true
      fi
      eval $arr_name='("${arr[@]}")'
    }

    local retval=$1
    local options
    local defaults

    IFS=';' read -r -a options <<< "$2"
    if [[ -z $3 ]]; then
      defaults=()
    else
      IFS=';' read -r -a defaults <<< "$3"
    fi
    local selected=()

    for ((i=0; i<${#options[@]}; i++)); do
      selected+=("${defaults[i]}")
      printf "\n"
    done

    # determine current screen position for overwriting the options
    local lastrow=`get_cursor_row`
    local startrow=$(($lastrow - ${#options[@]}))

    # 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 active=0
    while true; do
        # print options by overwriting the last lines
        local idx=0
        for option in "${options[@]}"; do
            local prefix="[ ]"
            if [[ ${selected[idx]} == true ]]; then
              prefix="[x]"
            fi

            cursor_to $(($startrow + $idx))
            if [ $idx -eq $active ]; then
                print_active "$option" "$prefix"
            else
                print_inactive "$option" "$prefix"
            fi
            ((idx++))
        done

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

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

    eval $retval='("${selected[@]}")'
}
1
Denis Semenenko

Da ich keine geeignete BASH-Alternative für Prompt-Toolkit (Python), Dialoguer (Rust) oder Enquirer (Node) gefunden habe, habe ich es selbst ausprobiert:

https://i.stack.imgur.com/6AyAI.png

https://asciinema.org/a/Y4hLxnN20JtAlrn3hsC6dCRn8https://Gist.github.com/blurayne/f63c5a8521c0eeab8e9afd8baa45c65e

1
user2452171

Ich habe das Beispiel von MestreLion verwendet und den folgenden Code entworfen. Sie müssen lediglich die Optionen und Aktionen in den ersten beiden Abschnitten aktualisieren.

#!/bin/bash
#title:         menu.sh
#description:   Menu which allows multiple items to be selected
#author:        Nathan Davieau
#               Based on script from MestreLion
#created:       May 19 2016
#updated:       N/A
#version:       1.0
#usage:         ./menu.sh
#==============================================================================

#Menu options
options[0]="AAA"
options[1]="BBB"
options[2]="CCC"
options[3]="DDD"
options[4]="EEE"

#Actions to take based on selection
function ACTIONS {
    if [[ ${choices[0]} ]]; then
        #Option 1 selected
        echo "Option 1 selected"
    fi
    if [[ ${choices[1]} ]]; then
        #Option 2 selected
        echo "Option 2 selected"
    fi
    if [[ ${choices[2]} ]]; then
        #Option 3 selected
        echo "Option 3 selected"
    fi
    if [[ ${choices[3]} ]]; then
        #Option 4 selected
        echo "Option 4 selected"
    fi
    if [[ ${choices[4]} ]]; then
        #Option 5 selected
        echo "Option 5 selected"
    fi
}

#Variables
ERROR=" "

#Clear screen for menu
clear

#Menu function
function MENU {
    echo "Menu Options"
    for NUM in ${!options[@]}; do
        echo "[""${choices[NUM]:- }""]" $(( NUM+1 ))") ${options[NUM]}"
    done
    echo "$ERROR"
}

#Menu loop
while MENU && read -e -p "Select the desired options using their number (again to uncheck, ENTER when done): " -n1 SELECTION && [[ -n "$SELECTION" ]]; do
    clear
    if [[ "$SELECTION" == *[[:digit:]]* && $SELECTION -ge 1 && $SELECTION -le ${#options[@]} ]]; then
        (( SELECTION-- ))
        if [[ "${choices[SELECTION]}" == "+" ]]; then
            choices[SELECTION]=""
        else
            choices[SELECTION]="+"
        fi
            ERROR=" "
    else
        ERROR="Invalid option: $SELECTION"
    fi
done

ACTIONS
1
Nathan Davieau