Frage Mehrfachauswahl im Bash-Skript


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

Im Wesentlichen möchte ich etwas ähnliches wie im folgenden Beispiel haben:

       #!/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 von http://www.faqs.org/docs/Linux-HOWTO/Bash-Prog-Intro-HOWTO.html#ss9.1)

Allerdings hätte mein Skript mehr Optionen, und ich möchte zulassen, dass mehrere ausgewählt werden. So etwas wie das:

1) Möglichkeit 1
2) Option 2
3) Option 3
4) Option 4
5) Fertig

Das Feedback zu den von ihnen ausgewählten Elementen wäre auch toll, zB Plus-Zeichen neben denen, die sie bereits ausgewählt haben. Wenn Sie beispielsweise "1" auswählen, möchte ich die Seite löschen und erneut drucken:

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

Dann, wenn Sie "3" wählen:

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

Wenn sie erneut (1) ausgewählt haben, möchte ich die Option "abwählen":

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 eine Liste der ausgewählten, die angezeigt werden sollen, bevor das Programm beendet wird, zB wenn der aktuelle Zustand ist:

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

Drücken von 5 sollte drucken:

Option 2, Option 3, Option 4

... und das Skript beendet.

Also meine Frage - ist das in bash möglich, und wenn ja, kann jemand ein Codebeispiel bereitstellen?

Jeder Rat würde sehr geschätzt werden.


24
2018-05-25 03:51


Ursprung




Antworten:


Ich denke, du solltest einen Blick darauf werfen Dialog oder Whiptail.

dialog box

Bearbeiten:

Hier ist ein Beispielskript, das die Optionen Ihrer Frage verwendet:

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

31
2018-05-25 04:49



Dank dafür. Sieht komplexer aus, als ich gehofft habe, aber ich werde es mir anschauen :-) - user38939
@ am2605: Siehe meine Bearbeitung. Ich habe ein Beispielskript hinzugefügt. - Dennis Williamson
Es sieht nur komplex aus, bis Sie es ein- oder zweimal benutzt haben, dann werden Sie nie wieder etwas anderes benutzen ... - Chris S


Wenn du denkst whiptail ist komplex, hier geht es um einen reinen Bash-Code genau was du willst. Es ist kurz (~ 20 Zeilen), aber ein bisschen kryptisch für einen Anfänger. Neben "+" für geprüfte Optionen, gibt es auch Feedback für jede Benutzeraktion ("ungültige Option", "Option X wurde aktiviert" / deaktiviert, usw.).

Das heißt, du gehst!

Ich hoffe, Sie genießen ... es war eine ziemlich lustige Herausforderung, es zu machen :)

#!/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
    [[ "$msg" ]] && echo "$msg"; :
}

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"

19
2017-08-06 02:04



Gut gemacht! Gut gemacht! - Daniel
Dieser ist ein wenig kryptisch, aber ich liebe Ihre Verwendung von komplexen Klammererweiterungen und dynamischen Arrays. Ich brauchte ein bisschen Zeit, um alles lesen zu können, aber ich liebe es. Ich mag auch die Tatsache, dass Sie die printf () Funktion eingebaut haben. Ich finde nicht viele, die darüber in bash wissen. Sehr praktisch, wenn man es gewohnt ist, in C zu codieren. - Yokai


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

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


5
2018-05-10 14:02



Schönes Beispiel! Wie schafft man es in KSH zu betreiben? - FuSsA
@ FuSsA: Ich habe meine Antwort bearbeitet, um die Änderungen anzuzeigen, die benötigt werden, damit es in ksh funktioniert. - Dennis Williamson
Das Array-Handling in bash ist sehr hardcore. Du bist nicht nur der Erste, du bist der einzige über 40k im ganzen Trinity. - peterh
@ FuSsA: options=(*) (oder andere Globbing-Muster) erhalten Sie eine Liste von Dateien im Array. Die Herausforderung wäre dann, das Auswahlmarkierungsfeld zu erhalten (${opts[@]}) mit ihm zusammen gezippt. Es kann mit einem durchgeführt werden for Schleife, aber es müsste für jeden Durchgang durch den äußeren ausgeführt werden while Schleife. Sie könnten in Betracht ziehen, zu verwenden dialog oder whiptail wie ich in meiner anderen Antwort erwähnt habe - obwohl dies externe Abhängigkeiten sind. - Dennis Williamson
@ FuSsA: Dann könnten Sie die Zeichenfolge in einem anderen Array speichern (oder verwenden ${opts[@]} und speichern Sie die Zeichenfolge, die als zusätzliches Argument an die Funktion übergeben wird, statt +). - Dennis Williamson


Ich schrieb eine Bibliothek namens FragebogenDies ist ein Mini-DSL zum Erstellen von Befehlszeilen-Fragebögen. Es fordert den Benutzer auf, eine Reihe von Fragen zu beantworten und die Antworten auf stdout auszudrucken.

Es macht Ihre Aufgabe wirklich einfach. Installieren Sie es mit pip install questionnaire und ein Skript erstellen, z.B. 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 fertig sind, beantworten Sie die Fragen, die sie ausgedruckt haben. 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 das möchte. Hier sind einige Features:

  • Gibt Antworten als JSON (oder als einfacher Text) als Standard aus
  • Ermöglicht Benutzern, zurückzugehen und Fragen erneut zu beantworten
  • Unterstützt bedingte Fragen (Fragen können von vorherigen Antworten abhängen)
  • Unterstützt die folgenden Arten von Fragen: rohe Eingabe, wählen Sie eine, wählen Sie viele
  • Keine obligatorische Kopplung zwischen Fragepräsentation und Antwortwerten

2
2017-07-21 15:38





Ich habe das Beispiel von MestreLion benutzt und den Code unten entworfen. Alles, was Sie tun müssen, ist die Aktualisierung der Optionen und Aktionen in den ersten beiden Abschnitten.

#!/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
2018-05-19 18:51



Ausgezeichnete Antwort. Fügen Sie auch eine Notiz hinzu, um die Nummer zu erhöhen, z. B. Option 15; woher n1 SELECTION ist der entscheidende Teil, um die Anzahl der Ziffern zu erhöhen. - dbf
Vergessen, hinzuzufügen; woher -n2 SELECTION akzeptiert zwei Ziffern (z. B. 15), -n3 akzeptiert drei (z. B. 153) usw. - dbf


export supermode=none

source easybashgui

list "Option 1" "Option 2" "Option 3" "Option 4"

-1
2018-05-10 07:54



Vielleicht könnten Sie eine kleine Beschreibung dessen hinzufügen, was das tut? Für zukünftige Besucher, nicht so sehr für das OP. - slm
Auch ein Link zum Ursprung von easybashgui. - Dennis Williamson