Wie fordere ich die Eingabe von Ja / Nein / Abbrechen in einem Linux-Shell-Skript auf?

1443

Ich möchte die Eingabe in einem Shell-Skript anhalten und den Benutzer zur Auswahl auffordern.
Der Standard Yes, Nooder CancelTyp Frage.
Wie erreiche ich dies in einer typischen Bash-Eingabeaufforderung?

Myrddin Emrys
quelle
11
Nur als Hinweis: Die Konvention für Eingabeaufforderungen ist so, dass, wenn Sie eine [yn]Option präsentieren, die aktivierte Option standardmäßig aktiviert ist, dh [Yn]standardmäßig "Ja" und [yN]standardmäßig "Nein". Siehe ux.stackexchange.com/a/40445/43532
Tyzoid
Jeder, der von ZSH hierher kommt, sieht in dieser Antwort, wie man den readBefehl zur Eingabeaufforderung verwendet
smac89

Antworten:

1618

Die einfachste und am weitesten verbreitete Methode, um Benutzereingaben an einer Shell-Eingabeaufforderung abzurufen, ist der readBefehl. Der beste Weg, um seine Verwendung zu veranschaulichen, ist eine einfache Demonstration:

while true; do
    read -p "Do you wish to install this program?" yn
    case $yn in
        [Yy]* ) make install; break;;
        [Nn]* ) exit;;
        * ) echo "Please answer yes or no.";;
    esac
done

Ein anderes Verfahren, wies darauf hin , durch Steven Huwig , ist Bash - selectBefehl. Hier ist das gleiche Beispiel mit select:

echo "Do you wish to install this program?"
select yn in "Yes" "No"; do
    case $yn in
        Yes ) make install; break;;
        No ) exit;;
    esac
done

Mit selectSie nicht brauchen die Eingabe zu sanieren - es zeigt die verfügbaren Optionen, und Sie eine Nummer eingeben , um Ihre Auswahl entspricht. Es wird auch automatisch while truewiederholt , sodass keine Wiederholung erforderlich ist, wenn eine ungültige Eingabe erfolgt.

Außerdem hat Léa Gris in ihrer Antwort einen Weg aufgezeigt, die Anforderungssprache agnostisch zu machen . Die Anpassung meines ersten Beispiels, um mehrere Sprachen besser bedienen zu können, könnte folgendermaßen aussehen:

set -- $(locale LC_MESSAGES)
yesptrn="$1"; noptrn="$2"; yesword="$3"; noword="$4"

while true; do
    read -p "Install (${yesword} / ${noword})? " yn
    case $yn in
        ${yesptrn##^} ) make install; break;;
        ${noptrn##^} ) exit;;
        * ) echo "Answer ${yesword} / ${noword}.";;
    esac
done

Offensichtlich bleiben andere Kommunikationszeichenfolgen hier unübersetzt (Installieren, Antworten), die in einer vollständigeren Übersetzung behandelt werden müssten, aber in vielen Fällen wäre sogar eine teilweise Übersetzung hilfreich.

Zum Schluss lesen Sie bitte die ausgezeichnete Antwort von F. Hauri .

Myrddin Emrys
quelle
30
Bei Verwendung von Bash in OS X Leopard habe ich geändert exit, breakum das Schließen der Registerkarte zu verhindern, wenn ich "Nein" ausgewählt habe.
Trey Piepmeier
1
Wie funktioniert dies mit Optionen, die länger als Ja oder Nein sind? Schreiben Sie in der case-Klausel etwas wie: Programm installieren und danach nichts tun) make install; brechen;
Shawn
1
@Shawn Mit read können Sie natürlich jedes Glob- oder Regex-Muster verwenden, das von der switch-Anweisung der Bash-Shell unterstützt wird. Es passt nur den eingegebenen Text an Ihre Muster an, bis eine Übereinstimmung gefunden wird. Mit select wird die Liste der Auswahlmöglichkeiten für den Befehl angegeben und dem Benutzer angezeigt. Sie können festlegen, dass die Elemente in der Liste so lang oder so kurz sind, wie Sie möchten. Ich empfehle, auf den Manpages nach umfassenden Nutzungsinformationen zu suchen.
Myrddin Emrys
3
Warum gibt es eine breakin der, selectwenn es keine Schleife gibt?
Jayen
1
@akostadinov Ich sollte wirklich meinen Bearbeitungsverlauf mehr lesen. Sie haben Recht, ich liege falsch, und ich habe einen Fehler eingeführt, der Jayans Rat folgt, hah. Der Fehler wurde später behoben, aber die Änderungen waren so weit voneinander entfernt, dass ich mich nicht einmal daran erinnerte, ihn geändert zu haben.
Myrddin Emrys
526

Mindestens fünf Antworten für eine allgemeine Frage.

Es hängt davon ab

  • konform: könnte auf schlechten Systemen mit generischen arbeiten Umgebungen
  • spezifisch: mit sogenannten bashismen

und wenn du willst

  • einfache "In-Line" -Frage / Antwort (generische Lösungen)
  • ziemlich formatierte Schnittstellen, wie oder grafischer mit libgtk oder libqt ...
  • Verwenden Sie die leistungsstarke Readline-Verlaufsfunktion

1. Generische POSIX-Lösungen

Sie können den readBefehl verwenden, gefolgt von if ... then ... else:

echo -n "Is this a good question (y/n)? "
read answer

# if echo "$answer" | grep -iq "^y" ;then

if [ "$answer" != "${answer#[Yy]}" ] ;then
    echo Yes
else
    echo No
fi

(Dank an Adam Katz 'Kommentar : Ersetzte den obigen Test durch einen, der tragbarer ist und eine Gabel vermeidet :)

POSIX, aber Single Key-Funktion

Aber wenn Sie nicht möchten, dass der Benutzer schlagen muss Return, können Sie schreiben:

( Bearbeitet: Wie @JonathanLeffler zu Recht vorschlägt, könnte es besser sein, die Konfiguration von stty zu speichern , als sie einfach zu zwingen, gesund zu werden .)

echo -n "Is this a good question (y/n)? "
old_stty_cfg=$(stty -g)
stty raw -echo ; answer=$(head -c 1) ; stty $old_stty_cfg # Careful playing with stty
if echo "$answer" | grep -iq "^y" ;then
    echo Yes
else
    echo No
fi

Hinweis: Dies wurde unter getestet, , , und !

Gleich, aber explizit warten auf yoder n:

#/bin/sh
echo -n "Is this a good question (y/n)? "
old_stty_cfg=$(stty -g)
stty raw -echo
answer=$( while ! head -c 1 | grep -i '[ny]' ;do true ;done )
stty $old_stty_cfg
if echo "$answer" | grep -iq "^y" ;then
    echo Yes
else
    echo No
fi

Verwenden dedizierter Tools

Es gibt viele Tools , die mit gebaut wurden libncurses, libgtk, libqtoder andere grafische Bibliotheken. Zum Beispiel mit whiptail:

if whiptail --yesno "Is this a good question" 20 60 ;then
    echo Yes
else
    echo No
fi

Abhängig von Ihrem System müssen Sie möglicherweise ein whiptailanderes ähnliches Tool ersetzen :

dialog --yesno "Is this a good question" 20 60 && echo Yes

gdialog --yesno "Is this a good question" 20 60 && echo Yes

kdialog --yesno "Is this a good question" 20 60 && echo Yes

Dabei 20ist die Höhe des Dialogfelds in Anzahl der Zeilen und 60die Breite des Dialogfelds angegeben. Diese Tools haben alle nahezu dieselbe Syntax.

DIALOG=whiptail
if [ -x /usr/bin/gdialog ] ;then DIALOG=gdialog ; fi
if [ -x /usr/bin/xdialog ] ;then DIALOG=xdialog ; fi
...
$DIALOG --yesno ...

2. Bash-spezifische Lösungen

Grundlegende In-Line- Methode

read -p "Is this a good question (y/n)? " answer
case ${answer:0:1} in
    y|Y )
        echo Yes
    ;;
    * )
        echo No
    ;;
esac

Ich bevorzuge die Verwendung, casedamit ich yes | ja | si | ouibei Bedarf sogar testen kann ...

im Einklang mit einzelnem Schlüsselmerkmal

Unter bash können wir die Länge der beabsichtigten Eingabe für den readBefehl angeben :

read -n 1 -p "Is this a good question (y/n)? " answer

Unter bash readakzeptiert der Befehl einen Timeout- Parameter, der nützlich sein kann.

read -t 3 -n 1 -p "Is this a good question (y/n)? " answer
[ -z "$answer" ] && answer="Yes"  # if 'yes' have to be default choice

3. Einige Tricks für spezielle Tools

Anspruchsvollere Dialogfelder, die über einfache yes - noZwecke hinausgehen :

dialog --menu "Is this a good question" 20 60 12 y Yes n No m Maybe

Fortschrittsanzeige:

dialog --gauge "Filling the tank" 20 60 0 < <(
    for i in {1..100};do
        printf "XXX\n%d\n%(%a %b %T)T progress: %d\nXXX\n" $i -1 $i
        sleep .033
    done
) 

Kleine Demo:

#!/bin/sh
while true ;do
    [ -x "$(which ${DIALOG%% *})" ] || DIALOG=dialog
    DIALOG=$($DIALOG --menu "Which tool for next run?" 20 60 12 2>&1 \
            whiptail       "dialog boxes from shell scripts" >/dev/tty \
            dialog         "dialog boxes from shell with ncurses" \
            gdialog        "dialog boxes from shell with Gtk" \
            kdialog        "dialog boxes from shell with Kde" ) || exit
    clear;echo "Choosed: $DIALOG."
    for i in `seq 1 100`;do
        date +"`printf "XXX\n%d\n%%a %%b %%T progress: %d\nXXX\n" $i $i`"
        sleep .0125
      done | $DIALOG --gauge "Filling the tank" 20 60 0
    $DIALOG --infobox "This is a simple info box\n\nNo action required" 20 60
    sleep 3
    if $DIALOG --yesno  "Do you like this demo?" 20 60 ;then
        AnsYesNo=Yes; else AnsYesNo=No; fi
    AnsInput=$($DIALOG --inputbox "A text:" 20 60 "Text here..." 2>&1 >/dev/tty)
    AnsPass=$($DIALOG --passwordbox "A secret:" 20 60 "First..." 2>&1 >/dev/tty)
    $DIALOG --textbox /etc/motd 20 60
    AnsCkLst=$($DIALOG --checklist "Check some..." 20 60 12 \
        Correct "This demo is useful"        off \
        Fun        "This demo is nice"        off \
        Strong        "This demo is complex"        on 2>&1 >/dev/tty)
    AnsRadio=$($DIALOG --radiolist "I will:" 20 60 12 \
        " -1" "Downgrade this answer"        off \
        "  0" "Not do anything"                on \
        " +1" "Upgrade this anser"        off 2>&1 >/dev/tty)
    out="Your answers:\nLike: $AnsYesNo\nInput: $AnsInput\nSecret: $AnsPass"
    $DIALOG --msgbox "$out\nAttribs: $AnsCkLst\nNote: $AnsRadio" 20 60
  done

Mehr Probe? Schauen Sie sich die Verwendung von Whiptail für die Auswahl des USB-Geräts und des USB-Wechselspeichers an: USBKeyChooser

5. Verwenden des Readline-Verlaufs

Beispiel:

#!/bin/bash

set -i
HISTFILE=~/.myscript.history
history -c
history -r

myread() {
    read -e -p '> ' $1
    history -s ${!1}
}
trap 'history -a;exit' 0 1 2 3 6

while myread line;do
    case ${line%% *} in
        exit )  break ;;
        *    )  echo "Doing something with '$line'" ;;
      esac
  done

Dadurch wird eine Datei erstellen .myscript.historyin Ihrem $HOMEVerzeichnis, als Sie Readline- Geschichte Befehle, wie nutzen könnten Up, Down, Ctrl+ rund andere.

F. Hauri
quelle
4
Beachten Sie, dass dies sttydie -gOption zur Verwendung bietet : old_stty=$(stty -g); stty raw -echo; …; stty "$old_stty". Dadurch wird die Einstellung genau so wiederhergestellt, wie sie gefunden wurde. Dies kann mit der Einstellung identisch sein oder nicht stty -sane.
Jonathan Leffler
3
read answerinterpretiert Backslashes vor Leerzeichen und Zeilenvorschüben und entfernt sie ansonsten, was selten beabsichtigt ist. Verwenden Sie read -r answerstattdessen gemäß SC2162 .
vlfig
1
Die Methode "Verwenden der Readline-Historie" ist für die Frage des OP so schrecklich ungeeignet. Ich bin froh, dass du es trotzdem aufgenommen hast. Ich habe Dutzende viel komplizierterer Skripte, die ich mit diesem Muster aktualisieren möchte!
Bruno Bronosky
5
Sie können sowohl casefür POSIX als auch für Bash verwenden (verwenden Sie eine Platzhalterbedingung anstelle einer Bash-Teilzeichenfolge :) case $answer in; [Yy]* ) echo Yes ;;, aber ich bevorzuge stattdessen die Verwendung einer bedingten Anweisung, die [ "$answer" != "${answer#[Yy]}" ]Ihre bevorzugt echo "$answer" | grep -iq ^y. Es ist portabler (einige Nicht-GNU-Greps werden nicht -qkorrekt implementiert ) und hat keinen Systemaufruf. ${answer#[Yy]}Verwendet die Parametererweiterung zum Entfernen Yoder yvon Anfang an $answerund verursacht eine Ungleichung, wenn eine der beiden vorhanden ist. Dies funktioniert in jeder POSIX-Shell (Dash, Ksh, Bash, Zsh, Busybox usw.).
Adam Katz
1
@CarterPape Ja, das war ein Witz! Aber in dieser detaillierten Antwort finden Sie möglicherweise viele Tipps (zweiter Punkt enthält mindestens 3 verschiedene Methoden)! Und ... ab ca. 5 Jahren erzählen Sie zuerst von meiner Zählmethode! ;-))
F. Hauri
349
echo "Please enter some input: "
read input_variable
echo "You entered: $input_variable"
Pistos
quelle
25
Ich bin anderer Meinung, da nur ein Teil der Funktionalität des Dialogfelds "Ja, Nein, Abbrechen" in DOS implementiert wird. Der Teil, den es nicht implementiert, ist die Eingabeprüfung ... Schleife, bis eine gültige Antwort empfangen wird.
Myrddin Emrys
1
(Der ursprüngliche
Fragentitel
8
Die ursprüngliche Beschreibung der Frage bleibt jedoch unverändert und wird immer um eine Antwort auf eine Ja / Nein / Abbrechen-Eingabeaufforderung gebeten. Der Titel wurde aktualisiert, um klarer als mein ursprünglicher zu sein, aber die Beschreibung der Frage war immer klar (meiner Meinung nach).
Myrddin Emrys
165

Sie können im internen Verwendung Lesebefehl; Verwenden Sie die -pOption, um den Benutzer mit einer Frage aufzufordern.

Seit BASH4 können Sie jetzt -ieine Antwort vorschlagen:

read -e -p "Enter the path to the file: " -i "/usr/local/etc/" FILEPATH
echo $FILEPATH

(Denken Sie jedoch daran, die Option "readline" -ezu verwenden, um die Zeilenbearbeitung mit den Pfeiltasten zu ermöglichen.)

Wenn Sie eine "Ja / Nein" -Logik wünschen, können Sie Folgendes tun:

read -e -p "
List the content of your home dir ? [Y/n] " YN

[[ $YN == "y" || $YN == "Y" || $YN == "" ]] && ls -la ~/
yPhil
quelle
6
Es ist zu beachten, dass dies FILEPATHder von Ihnen gewählte Variablenname ist und mit der Antwort auf die Eingabeaufforderung festgelegt wird. Wenn Sie dann beispielsweise ausführen würden vlc "$FILEPATH", vlcwürde diese Datei geöffnet.
Ken Sharp
Was ist der Vorteil -eim zweiten Beispiel (einfaches Ja / Nein)?
JBallin
Gibt es einen Grund, -e -panstatt zu verwenden -ep?
JBallin
1
Ohne das -eFlag / die Option können Sie (abhängig von der Implementierung) möglicherweise nicht "y" eingeben und dann Ihre Meinung ändern und durch ein "n" (oder irgendetwas anderes) ersetzen. Wenn Sie einen Befehl dokumentieren, ist es unter anderem aus Gründen der Lesbarkeit / Klarheit besser, die Optionen separat aufzulisten.
yPhil
109

Bash hat wählen für diesen Zweck.

select result in Yes No Cancel
do
    echo $result
done
Steven Huwig
quelle
18
+1 Genial einfache Lösung. Einzige Sache: Dies wird auffordern und auffordern und auffordern ... bis Sie ein exitInside hinzufügen :)
Kaiser
5
(kaiser: Um davon abzubrechen, geben Sie einfach die EOT ein: Ctrl-DAber natürlich benötigt echter Code, der ihn verwendet, eine Unterbrechung oder einen Ausgang im Körper.)
Zorawar
12
Dies erlaubt Ihnen jedoch nicht, y oder n einzugeben. Sie wählen, indem Sie 1 2 oder 3
eingeben
3
exitwird das Skript alle zusammen breakwhilecase
beenden
57
read -p "Are you alright? (y/n) " RESP
if [ "$RESP" = "y" ]; then
  echo "Glad to hear it"
else
  echo "You need more bash programming"
fi
serg
quelle
36

Folgendes habe ich zusammengestellt:

#!/bin/sh

promptyn () {
    while true; do
        read -p "$1 " yn
        case $yn in
            [Yy]* ) return 0;;
            [Nn]* ) return 1;;
            * ) echo "Please answer yes or no.";;
        esac
    done
}

if promptyn "is the sky blue?"; then
    echo "yes"
else
    echo "no"
fi

Ich bin ein Anfänger, also nimm das mit einem Körnchen Salz, aber es scheint zu funktionieren.

mpen
quelle
9
Wenn Sie zu wechseln case $yn in, case ${yn:-$2} inkönnen Sie das zweite Argument als Standardwert verwenden, Y oder N.
jchook
1
oder ändern Sie case $ynzu " case "${yn:-Y}"Ja" als Standard
rubo77
35
inquire ()  {
  echo  -n "$1 [y/n]? "
  read answer
  finish="-1"
  while [ "$finish" = '-1' ]
  do
    finish="1"
    if [ "$answer" = '' ];
    then
      answer=""
    else
      case $answer in
        y | Y | yes | YES ) answer="y";;
        n | N | no | NO ) answer="n";;
        *) finish="-1";
           echo -n 'Invalid response -- please reenter:';
           read answer;;
       esac
    fi
  done
}

... other stuff

inquire "Install now?"

...
SumoRunner
quelle
1
Fügen Sie am Anfang jeder Zeile vier Leerzeichen ein, um die Formatierung des Codes beizubehalten.
Jouni K. Seppänen
10
Warum geben wir 'y' und 'n' als Parameter an, um abzufragen (), ob die Fallschalter fest codiert sind? Das bittet nur um Missbrauch. Da es sich um feste Parameter handelt, die nicht geändert werden können, sollte das Echo in Zeile 2 lauten: echo -n "$ 1 [J / N]?" Sie können nicht geändert werden, daher sollten sie nicht geliefert werden.
Myrddin Emrys
1
@MyrddinEmrys Könnten Sie bitte Ihren Kommentar ausarbeiten? Oder poste einen Link zu einem Artikel oder ein paar Keywords, damit ich selbst recherchieren kann.
Mateusz Piotrowski
4
@MateuszPiotrowski Die Antwort wurde bearbeitet und verbessert, seit ich meinen Kommentar abgegeben habe. Sie können oben auf den Link "bearbeitet am 23. Dezember" klicken, um alle früheren Versionen dieser Antwort anzuzeigen. Im Jahr 2008 war der Code ganz anders.
Myrddin Emrys
27

Sie wollen:

  • Bash eingebaute Befehle (dh portabel)
  • Überprüfen Sie TTY
  • Standardantwort
  • Auszeit
  • Farbige Frage

Snippet

do_xxxx=y                      # In batch mode => Default is Yes
[[ -t 0 ]] &&                  # If TTY => Prompt the question
read -n 1 -p $'\e[1;32m
Do xxxx? (Y/n)\e[0m ' do_xxxx  # Store the answer in $do_xxxx
if [[ $do_xxxx =~ ^(y|Y|)$ ]]  # Do if 'y' or 'Y' or empty
then
    xxxx
fi

Erklärungen

  • [[ -t 0 ]] && read ...=> Befehl readaufrufen, wenn TTY
  • read -n 1 => Warte auf ein Zeichen
  • $'\e[1;32m ... \e[0m '=> Drucken in Grün
    (Grün ist in Ordnung, da es auf beiden weißen / schwarzen Hintergründen lesbar ist)
  • [[ $do_xxxx =~ ^(y|Y|)$ ]] => Bash Regex

Zeitüberschreitung => Standardantwort ist Nein

do_xxxx=y
[[ -t 0 ]] && {                   # Timeout 5 seconds (read -t 5)
read -t 5 -n 1 -p $'\e[1;32m
Do xxxx? (Y/n)\e[0m ' do_xxxx ||  # read 'fails' on timeout
do_xxxx=n ; }                     # Timeout => answer No
if [[ $do_xxxx =~ ^(y|Y|)$ ]]
then
    xxxx
fi
olibre
quelle
26

Der einfachste Weg, dies mit der geringsten Anzahl von Zeilen zu erreichen, ist wie folgt:

read -p "<Your Friendly Message here> : y/n/cancel" CONDITION;

if [ "$CONDITION" == "y" ]; then
   # do something here!
fi

Das ifist nur ein Beispiel: es ist an Ihnen , wie Sie diese Variable behandeln.

Apurv Nerlekar
quelle
20

Verwenden Sie den readBefehl:

echo Would you like to install? "(Y or N)"

read x

# now check if $x is "y"
if [ "$x" = "y" ]; then
    # do something here!
fi

und dann all die anderen Sachen, die du brauchst

ThatLinuxGuy
quelle
17

Diese Lösung liest ein einzelnes Zeichen und ruft eine Funktion für eine Ja-Antwort auf.

read -p "Are you sure? (y/n) " -n 1
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
    do_something      
fi
Dennis
quelle
2
@Jav das Echo druckt nach Ihrer Antwort eine neue Zeile. Ohne sie wird das nächste zu druckende Element unmittelbar nach Ihrer Antwort in derselben Zeile angezeigt. Entfernen Sie das echo, um sich selbst davon zu überzeugen.
Dennis
13

Eine schöne ncurses-like inputbox Verwenden Sie den Befehl erhalten Dialog wie folgt aus :

#!/bin/bash
if (dialog --title "Message" --yesno "Want to do something risky?" 6 25)
# message box will have the size 25x6 characters
then 
    echo "Let's do something risky"
    # do something risky
else 
    echo "Let's stay boring"
fi

Das Dialogpaket wird standardmäßig mindestens unter SUSE Linux installiert. Sieht aus wie: der Befehl "dialog" in Aktion

Thorsten Staerk
quelle
12
read -e -p "Enter your choice: " choice

Mit dieser -eOption kann der Benutzer die Eingabe mit den Pfeiltasten bearbeiten.

Wenn Sie einen Vorschlag als Eingabe verwenden möchten:

read -e -i "yes" -p "Enter your choice: " choice

-i Option druckt eine suggestive Eingabe.

Jahid
quelle
yap, -e -iarbeite nicht in sh (Bourne Shell), aber die Frage ist Bash spezifisch markiert.
Jahid
12

Sie können die Standardeinstellung REPLYfür a verwenden read, in Kleinbuchstaben konvertieren und mit einer Reihe von Variablen mit einem Ausdruck vergleichen.
Das Skript unterstützt auch ja/ si/oui

read -rp "Do you want a demo? [y/n/c] "

[[ ${REPLY,,} =~ ^(c|cancel)$ ]] && { echo "Selected Cancel"; exit 1; }

if [[ ${REPLY,,} =~ ^(y|yes|j|ja|s|si|o|oui)$ ]]; then
   echo "Positive"
fi
Walter A.
quelle
12

Es ist möglich, eine länderbezogene "Ja / Nein-Auswahl" in einer POSIX-Shell zu verarbeiten. Mithilfe der Einträge der LC_MESSAGESGebietsschemakategorie stellt witch vorgefertigte RegEx-Muster bereit, die mit einer Eingabe übereinstimmen, sowie Zeichenfolgen für das lokalisierte Ja Nein.

#!/usr/bin/env sh

# Getting LC_MESSAGES values into variables
# shellcheck disable=SC2046 # Intended IFS splitting
IFS='
' set -- $(locale LC_MESSAGES)

yesexpr="$1"
noexpr="$2"
yesstr="$3"
nostr="$4"
messages_codeset="$5" # unused here, but kept as documentation

# Display Yes / No ? prompt into locale
echo "$yesstr / $nostr ?"

# Read answer
read -r yn

# Test answer
case "$yn" in
# match only work with the character class from the expression
  ${yesexpr##^}) echo "answer $yesstr" ;;
  ${noexpr##^}) echo "answer $nostr" ;;
esac

EDIT: Wie @Urhixidur in seinem Kommentar erwähnt :

Leider gibt POSIX nur die ersten beiden an (yesexpr und noexpr). Unter Ubuntu 16 sind yesstr und nostr leer.

Siehe: https://www.ee.ryerson.ca/~courses/ele709/susv4/xrat/V4_xbd_chap07.html#tag_21_07_03_06

LC_MESSAGES

Die Schlüsselwörter yesstrund nostrlocale sowie die Elemente YESSTRund NOSTRlanginfo wurden früher verwendet, um positive und negative Antworten des Benutzers abzugleichen. In POSIX.1-2008, die yesexpr, noexpr, YESEXPR, und NOEXPRerweiterte reguläre Ausdrücke haben sie ersetzt. Anwendungen sollten die allgemeinen, auf dem Gebietsschema basierenden Nachrichtenfunktionen verwenden, um Aufforderungsnachrichten auszugeben, die Beispielantworten enthalten.

Alternativ können Sie Gebietsschemas auf Bash-Weise verwenden:

#!/usr/bin/env bash

IFS=$'\n' read -r -d '' yesexpr noexpr _ < <(locale LC_MESSAGES)

printf -v yes_or_no_regex "(%s)|(%s)" "$yesexpr" "$noexpr"

printf -v prompt $"Please answer Yes (%s) or No (%s): " "$yesexpr" "$noexpr"

declare -- answer=;

until [[ "$answer" =~ $yes_or_no_regex ]]; do
  read -rp "$prompt" answer
done

if [[ -n "${BASH_REMATCH[1]}" ]]; then
  echo $"You answered: Yes"
else
  echo $"No, was your answer."
fi

Die Antwort wird mithilfe der bereitgestellten regulären Ausdrücke der Gebietsschemaumgebung abgeglichen.

Um die verbleibenden Nachrichten zu übersetzen bash --dump-po-strings scriptname, geben Sie die Po-Zeichenfolgen für die Lokalisierung aus:

#: scriptname:8
msgid "Please answer Yes (%s) or No (%s): "
msgstr ""
#: scriptname:17
msgid "You answered: Yes"
msgstr ""
#: scriptname:19
msgid "No, was your answer."
msgstr ""
Léa Gris
quelle
Ich liebe die Hinzufügung einer sprachunabhängigen Option. Gut gemacht.
Myrddin Emrys
1
Leider gibt POSIX nur die ersten beiden an (yesexpr und noexpr). Unter Ubuntu 16 sind yesstr und nostr leer.
Urhixidur
1
Aber warte! Es gibt schlimmere Neuigkeiten! Die Bash-Case-Anweisungsausdrücke sind nicht regulär, sondern Dateinamenausdrücke. Yesexpr und noexpr von Ubuntu 16 ("^ [yY]. *" Und "^ [nN]. *") Werden daher aufgrund der eingebetteten Periode völlig fehlschlagen. In einem regulären Ausdruck bedeutet ". *" "Jedes Nicht-Zeilenumbruchzeichen, null oder mehrmals". Aber in einer Fallaussage ist es ein wörtliches "." gefolgt von einer beliebigen Anzahl von Zeichen.
Urhixidur
1
Das Beste, was mit yesexprund noexprin einer Shell-Umgebung gemacht werden kann, ist, es in Bashs spezifischem RegEx-Matching zu verwendenif [[ "$yn" =~ $yesexpr ]]; then echo $"Answered yes"; else echo $"Answered no"; fi
Léa Gris
10

Nur ein Tastendruck

Hier ist ein längerer, aber wiederverwendbarer und modularer Ansatz:

  • Rückgabe 0= ja und 1= nein
  • Kein Drücken der Eingabetaste erforderlich - nur ein einzelnes Zeichen
  • Kann drücken enter, um die Standardauswahl zu akzeptieren
  • Kann die Standardauswahl deaktivieren, um eine Auswahl zu erzwingen
  • Funktioniert für beide zshund bash.

Standardmäßig "Nein", wenn Sie die Eingabetaste drücken

Beachten Sie, dass das Ngroß geschrieben ist. Hier wird die Eingabetaste gedrückt, wobei die Standardeinstellung übernommen wird:

$ confirm "Show dangerous command" && echo "rm *"
Show dangerous command [y/N]?

Beachten Sie auch, dass dies [y/N]?automatisch angehängt wurde. Das Standard "Nein" wird akzeptiert, daher wird nichts wiedergegeben.

Erneut auffordern, bis eine gültige Antwort gegeben wird:

$ confirm "Show dangerous command" && echo "rm *"
Show dangerous command [y/N]? X
Show dangerous command [y/N]? y
rm *

Standardmäßig "Ja", wenn Sie die Eingabetaste drücken

Beachten Sie, dass das Ygroß geschrieben wird:

$ confirm_yes "Show dangerous command" && echo "rm *"
Show dangerous command [Y/n]?
rm *

Oben habe ich nur die Eingabetaste gedrückt, sodass der Befehl ausgeführt wurde.

Kein Standard ein enter- erfordern yodern

$ get_yes_keypress "Here you cannot press enter. Do you like this [y/n]? "
Here you cannot press enter. Do you like this [y/n]? k
Here you cannot press enter. Do you like this [y/n]?
Here you cannot press enter. Do you like this [y/n]? n
$ echo $?
1

Hier 1oder falsch wurde zurückgegeben. Beachten Sie, dass Sie mit dieser untergeordneten Funktion Ihre eigenen Funktionen bereitstellen müssen[y/n]? Eingabeaufforderung .

Code

# Read a single char from /dev/tty, prompting with "$*"
# Note: pressing enter will return a null string. Perhaps a version terminated with X and then remove it in caller?
# See https://unix.stackexchange.com/a/367880/143394 for dealing with multi-byte, etc.
function get_keypress {
  local REPLY IFS=
  >/dev/tty printf '%s' "$*"
  [[ $ZSH_VERSION ]] && read -rk1  # Use -u0 to read from STDIN
  # See https://unix.stackexchange.com/q/383197/143394 regarding '\n' -> ''
  [[ $BASH_VERSION ]] && </dev/tty read -rn1
  printf '%s' "$REPLY"
}

# Get a y/n from the user, return yes=0, no=1 enter=$2
# Prompt using $1.
# If set, return $2 on pressing enter, useful for cancel or defualting
function get_yes_keypress {
  local prompt="${1:-Are you sure [y/n]? }"
  local enter_return=$2
  local REPLY
  # [[ ! $prompt ]] && prompt="[y/n]? "
  while REPLY=$(get_keypress "$prompt"); do
    [[ $REPLY ]] && printf '\n' # $REPLY blank if user presses enter
    case "$REPLY" in
      Y|y)  return 0;;
      N|n)  return 1;;
      '')   [[ $enter_return ]] && return "$enter_return"
    esac
  done
}

# Credit: http://unix.stackexchange.com/a/14444/143394
# Prompt to confirm, defaulting to NO on <enter>
# Usage: confirm "Dangerous. Are you sure?" && rm *
function confirm {
  local prompt="${*:-Are you sure} [y/N]? "
  get_yes_keypress "$prompt" 1
}    

# Prompt to confirm, defaulting to YES on <enter>
function confirm_yes {
  local prompt="${*:-Are you sure} [Y/n]? "
  get_yes_keypress "$prompt" 0
}
Tom Hale
quelle
Als ich dieses Skript getestet habe, habe ich anstelle des obigen Oututs Show dangerous command [y/N]? [y/n]?undShow dangerous command [Y/n]? [y/n]?
Ilias Karim
Danke @IliasKarim, das habe ich gerade behoben.
Tom Hale
9

Entschuldigung für die Veröffentlichung in einem so alten Beitrag. Vor einigen Wochen hatte ich ein ähnliches Problem. In meinem Fall brauchte ich eine Lösung, die auch in einem Online-Installationsskript funktioniert, z.curl -Ss https://raw.github.com/_____/installer.sh | bash

Verwenden read yesno < /dev/ttyfunktioniert gut für mich:

echo -n "These files will be uploaded. Is this ok? (y/n) "
read yesno < /dev/tty

if [ "x$yesno" = "xy" ];then

   # Yes
else

   # No
fi

Hoffe das hilft jemandem.

user9869932
quelle
Ein wichtiger Teil davon ist die Eingabevalidierung. Ich denke, mein erstes Beispiel so anzupassen, dass ttyEingaben akzeptiert werden, wie Sie es getan haben, hätte sich auch für Sie bewährt und auch Schleifen bei schlechten Eingaben erhalten (stellen Sie sich ein paar Zeichen im Puffer vor; Ihre Methode würde den Benutzer zwingen, immer Nein zu wählen).
Myrddin Emrys
7

Mir ist aufgefallen, dass niemand eine Antwort mit einem mehrzeiligen Echo-Menü für solch einfache Benutzereingaben gepostet hat.

#!/bin/bash

function ask_user() {    

echo -e "
#~~~~~~~~~~~~#
| 1.) Yes    |
| 2.) No     |
| 3.) Quit   |
#~~~~~~~~~~~~#\n"

read -e -p "Select 1: " choice

if [ "$choice" == "1" ]; then

    do_something

elif [ "$choice" == "2" ]; then

    do_something_else

elif [ "$choice" == "3" ]; then

    clear && exit 0

else

    echo "Please select 1, 2, or 3." && sleep 3
    clear && ask_user

fi
}

ask_user

Diese Methode wurde in der Hoffnung veröffentlicht, dass jemand sie nützlich und zeitsparend findet.

Yokai
quelle
4

Multiple-Choice-Version:

ask () {                        # $1=question $2=options
    # set REPLY
    # options: x=..|y=..
    while $(true); do
        printf '%s [%s] ' "$1" "$2"
        stty cbreak
        REPLY=$(dd if=/dev/tty bs=1 count=1 2> /dev/null)
        stty -cbreak
        test "$REPLY" != "$(printf '\n')" && printf '\n'
        (
            IFS='|'
            for o in $2; do
                if [ "$REPLY" = "${o%%=*}" ]; then
                    printf '\n'
                    break
                fi
            done
        ) | grep ^ > /dev/null && return
    done
}

Beispiel:

$ ask 'continue?' 'y=yes|n=no|m=maybe'
continue? [y=yes|n=no|m=maybe] g
continue? [y=yes|n=no|m=maybe] k
continue? [y=yes|n=no|m=maybe] y
$

Es wird REPLYauf y(innerhalb des Skripts) gesetzt.

Ernest A.
quelle
4

Ich schlage vor, Sie verwenden Dialog ...

Linux Apprentice: Verbessern Sie Bash Shell-Skripte mithilfe des Dialogfelds

Der Dialogbefehl ermöglicht die Verwendung von Fensterfeldern in Shell-Skripten, um deren Verwendung interaktiver zu gestalten.

Es ist einfach und benutzerfreundlich. Es gibt auch eine Gnome-Version namens gdialog, die genau dieselben Parameter verwendet, aber den GUI-Stil auf X zeigt.

Osama Al-Maadeed
quelle
Schauen Sie sich für den Gelegenheitsleser mit dem Dialogbefehl hier ein Snippet an: stackoverflow.com/a/22893526/363573
Stephan
4

Inspiriert von den Antworten von @Mark und @Myrddin habe ich diese Funktion für eine universelle Eingabeaufforderung erstellt

uniprompt(){
    while true; do
        echo -e "$1\c"
        read opt
        array=($2)
        case "${array[@]}" in  *"$opt"*) eval "$3=$opt";return 0;; esac
        echo -e "$opt is not a correct value\n"
    done
}

benutze es so:

unipromtp "Select an option: (a)-Do one (x)->Do two (f)->Do three : " "a x f" selection
echo "$selection"
Miguel
quelle
4

allgemeiner wäre:

function menu(){
    title="Question time"
    prompt="Select:"
    options=("Yes" "No" "Maybe")
    echo "$title"
    PS3="$prompt"
    select opt in "${options[@]}" "Quit/Cancel"; do
        case "$REPLY" in
            1 ) echo "You picked $opt which is option $REPLY";;
            2 ) echo "You picked $opt which is option $REPLY";;
            3 ) echo "You picked $opt which is option $REPLY";;
            $(( ${#options[@]}+1 )) ) clear; echo "Goodbye!"; exit;;
            *) echo "Invalid option. Try another one.";continue;;
         esac
     done
     return
}
Alexander Löfqvist
quelle
3

Eine einfache Möglichkeit, dies zu tun, ist mit xargs -poder gnu parallel --interactive.

Ich mag das Verhalten von xargs ein wenig besser, weil es jeden Befehl unmittelbar nach der Eingabeaufforderung wie andere interaktive Unix-Befehle ausführt, anstatt die Yesses zu sammeln, die am Ende ausgeführt werden sollen. (Sie können Strg-C drücken, nachdem Sie die gewünschten durchlaufen haben.)

z.B,

echo *.xml | xargs -p -n 1 -J {} mv {} backup/
Joshua Goldberg
quelle
Nicht schlecht, aber xargs --interactivebeschränkt auf Ja oder Nein. Solange das alles ist, was Sie brauchen, kann das genug sein, aber meine ursprüngliche Frage gab ein Beispiel mit drei möglichen Ergebnissen. Ich mag es wirklich, dass es streambar ist; Viele gängige Szenarien würden von der Fähigkeit zur Weiterleitung profitieren.
Myrddin Emrys
Aha. Meiner Meinung nach sollte "Abbrechen" einfach jede weitere Ausführung stoppen, was dies über Strg-C unterstützt. Wenn Sie jedoch beim Abbrechen (oder bei Nein) etwas Komplexeres tun müssen, reicht dies nicht aus.
Joshua Goldberg
3

Als Freund eines einzeiligen Befehls habe ich Folgendes verwendet:

while [ -z $prompt ]; do read -p "Continue (y/n)?" choice;case "$choice" in y|Y ) prompt=true; break;; n|N ) exit 0;; esac; done; prompt=;

Langform geschrieben, funktioniert es so:

while [ -z $prompt ];
  do read -p "Continue (y/n)?" choice;
  case "$choice" in
    y|Y ) prompt=true; break;;
    n|N ) exit 0;;
  esac;
done;
prompt=;
ccDict
quelle
Können Sie die Verwendung der Eingabeaufforderungsvariablen verdeutlichen? Sieht für mich so aus, als wäre es nach dem einen Liner abgewischt. Wie benutzt man die Linie also, um etwas zu tun?
Myrddin Emrys
Die Eingabeaufforderung wird nach der while-Schleife gelöscht. Weil ich möchte, dass die Eingabeaufforderungsvariable anschließend initialisiert wird (da ich die Anweisung häufiger verwende). Wenn diese Zeile in einem Shell-Skript enthalten ist, wird nur fortgefahren, wenn y | Y eingegeben wird, und beendet, wenn n | N eingegeben wird, oder es wird wiederholt nach der Eingabe für alles andere gefragt.
ccDict
3

Ich habe die caseAussage in einem solchen Szenario ein paar Mal verwendet. Die Verwendung der Fallanweisung ist ein guter Weg, um dies zu erreichen. Eine whileSchleife, die den caseBlock kapselt und eine boolesche Bedingung verwendet, kann implementiert werden, um das Programm noch besser steuern zu können und viele andere Anforderungen zu erfüllen. Nachdem alle Bedingungen erfüllt sind, breakkann ein verwendet werden, das die Steuerung an den Hauptteil des Programms zurückgibt. Um andere Bedingungen zu erfüllen, können natürlich auch bedingte Anweisungen hinzugefügt werden, die die Kontrollstrukturen begleiten: caseAnweisung und mögliche whileSchleife.

Beispiel für die Verwendung einer caseAnweisung zur Erfüllung Ihrer Anfrage

#! /bin/sh 

# For potential users of BSD, or other systems who do not
# have a bash binary located in /bin the script will be directed to
# a bourne-shell, e.g. /bin/sh

# NOTE: It would seem best for handling user entry errors or
# exceptions, to put the decision required by the input 
# of the prompt in a case statement (case control structure), 

echo Would you like us to perform the option: "(Y|N)"

read inPut

case $inPut in
    # echoing a command encapsulated by 
    # backticks (``) executes the command
    "Y") echo `Do something crazy`
    ;;
    # depending on the scenario, execute the other option
    # or leave as default
    "N") echo `execute another option`
    ;;
esac

exit
oOpSgEo
quelle
3

Ja / Nein / Abbrechen

Funktion

#!/usr/bin/env bash
@confirm() {
  local message="$*"
  local result=''

  echo -n "> $message (Yes/No/Cancel) " >&2

  while [ -z "$result" ] ; do
    read -s -n 1 choice
    case "$choice" in
      y|Y ) result='Y' ;;
      n|N ) result='N' ;;
      c|C ) result='C' ;;
    esac
  done

  echo $result
}

Verwendungszweck

case $(@confirm 'Confirm?') in
  Y ) echo "Yes" ;;
  N ) echo "No" ;;
  C ) echo "Cancel" ;;
esac

Bestätigen Sie mit sauberen Benutzereingaben

Funktion

#!/usr/bin/env bash
@confirm() {
  local message="$*"
  local result=3

  echo -n "> $message (y/n) " >&2

  while [[ $result -gt 1 ]] ; do
    read -s -n 1 choice
    case "$choice" in
      y|Y ) result=0 ;;
      n|N ) result=1 ;;
    esac
  done

  return $result
}

Verwendungszweck

if @confirm 'Confirm?' ; then
  echo "Yes"
else
  echo "No"
fi
Eduardo Cuomo
quelle
2
yn() {
  if [[ 'y' == `read -s -n 1 -p "[y/n]: " Y; echo $Y` ]];
  then eval $1;
  else eval $2;
  fi }
yn 'echo yes' 'echo no'
yn 'echo absent no function works too!'
jlettvin
quelle
Dies scheint komplex und fragil. Wie wäre es nuryn(){ read -s -n 1 -p '[y/n]'; test "$REPLY" = "y" ; } yn && echo success || echo failure
Tripleee
2

Als Antwort auf andere:

Sie müssen in BASH4 keine Groß- und Kleinschreibung angeben. Verwenden Sie einfach das ',,', um eine var-Kleinbuchstabe zu erstellen. Außerdem mag ich es nicht, Code in den Leseblock zu setzen, das Ergebnis zu erhalten und außerhalb des Leseblocks IMO damit umzugehen. Fügen Sie auch ein 'q' ein, um IMO zu beenden. Zuletzt, warum 'Ja' eingeben, einfach -n1 verwenden und y drücken.

Beispiel: Der Benutzer kann y / n und auch q drücken, um einfach zu beenden.

ans=''
while true; do
    read -p "So is MikeQ the greatest or what (y/n/q) ?" -n1 ans
    case ${ans,,} in
        y|n|q) break;;
        *) echo "Answer y for yes / n for no  or q for quit.";;
    esac
done

echo -e "\nAnswer = $ans"

if [[ "${ans,,}" == "q" ]] ; then
        echo "OK Quitting, we will assume that he is"
        exit 0
fi

if [[ "${ans,,}" == "y" ]] ; then
        echo "MikeQ is the greatest!!"
else
        echo "No? MikeQ is not the greatest?"
fi
Mike Q.
quelle
0

In solchen Szenarien müssen Sie das Skript meistens so lange ausführen, bis der Benutzer weiterhin "Ja" eingibt, und erst dann anhalten, wenn der Benutzer "Nein" eingibt. Das folgende Snippet würde Ihnen dabei helfen!

#!/bin/bash
input="yes"
while [ "$input" == "yes" ]
do
  echo "execute script functionality here..!!"
  echo "Do you want to continue (yes/no)?"
  read input
done
Azhar Ansari
quelle