Как мне запросить ввод Yes/No/Cancel в оболочке Linux script?

Я хочу приостановить ввод в оболочке script и запросить пользователя для выбора. Стандартный вопрос "Да, нет или Отменить". Как это сделать в типичном приглашении bash?

Ответы

Ответ 1

Самым простым и наиболее доступным способом получения пользовательского ввода в приглашении оболочки является команда read. Лучший способ проиллюстрировать его использование - это простая демонстрация:

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

Другой метод, указанный Стивеном Хувигом Steven Huwig, - это команда Bash select. Вот тот же пример с использованием 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

С select вам не нужно очищать входные данные & ndash; он отображает доступные варианты, и вы вводите число, соответствующее вашему выбору. Он также выполняет циклы автоматически, поэтому нет необходимости повторять цикл while true, если они дают неверный ввод.

Кроме того, Леа Гри продемонстрировала способ сделать язык запроса независимым в ее ответе. Адаптация моего первого примера для лучшего обслуживания нескольких языков может выглядеть следующим образом:

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

Очевидно, что другие коммуникационные строки здесь остаются непереведенными (Установить, Ответить), которые необходимо учитывать при более полном завершении перевода, но во многих случаях даже частичный перевод будет полезен.

Наконец, ознакомьтесь с отличным ответом от F. Hauri.

Ответ 2

По крайней мере пять ответов на один общий вопрос.

В зависимости от

  • : может работать на плохих системах с общими средами
  • специфический: использование так называемых базизмов

и если вы хотите

  • простой '' в строке '' вопрос/ответ (общие решения)
  • довольно отформатированные интерфейсы, такие как или более графически с использованием libgtk или libqt...
  • использовать мощную способность чтения readline

1. Общие решения POSIX

Вы можете использовать команду read, а затем 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

(Спасибо Адаму Катцу: Замените тест выше тем, который более переносим и избегает одной вилки :)

POSIX, но одна ключевая функция

Но если вы не хотите, чтобы пользователь ударил Return, вы могли бы написать:

(Отредактировано: как @JonathanLeffler справедливо предлагает, сохранение stty конфигурации могло бы быть лучше, чем просто заставить их нормальным.)

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

Примечание. Это было проверено в , , , и !

То же, но явно ожидая для y или 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

Использование специальных инструментов

Существует множество инструментов, которые были созданы с использованием libncurses, libgtk, libqt или других графических библиотек. Например, используя whiptail:

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

В зависимости от вашей системы вам может потребоваться заменить whiptail другим аналогичным инструментом:

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

где 20 - высота диалогового окна в количестве строк, а 60 - ширина диалогового окна. Все эти инструменты имеют одинаковый синтаксис.

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

2. Специальные решения Bash

Основной метод

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

Я предпочитаю использовать case чтобы я мог даже проверить на yes | ja | si | oui yes | ja | si | oui yes | ja | si | oui при необходимости...

в соответствии с одной ключевой функцией

В разделе bash мы можем указать длину предполагаемого ввода для команды read:

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

В bash команда read принимает параметр таймаута, который может быть полезен.

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

Некоторые трюки для выделенных инструментов

Более сложные диалоговые окна, помимо простых yes - no целей:

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

Индикатор:

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
) 

Маленькая демонстрация:

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

Еще пример? Посмотрите, как использовать whiptail для выбора USB-устройства и USB-съемного переключателя хранения: USBKeyChooser

5. Использование истории чтения

Пример:

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

Это создаст файл .myscript.history в вашем каталоге $HOME, чем вы могли бы использовать команды истории чтения, такие как Up, Down, Ctrl + r и другие.

Ответ 3

echo "Please enter some input: "
read input_variable
echo "You entered: $input_variable"

Ответ 4

Вы можете использовать встроенную команду -i n read; Используйте параметр -p чтобы -p у пользователя вопрос.

Начиная с BASH4, теперь вы можете использовать -i чтобы предложить ответ, поэтому пользователю нужно только нажать return чтобы ввести его:

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

(Но не забудьте использовать опцию "readline" -e чтобы разрешить редактирование строк со стрелками)

Если вы хотите логику "да/нет", вы можете сделать что-то вроде этого:

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

[[ $YN == "y" || $YN == "Y" || $YN == "" ]] && ls -la ~/

Ответ 5

Bash выберите для этой цели.

select result in Yes No Cancel
do
    echo $result
done

Ответ 6

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

Ответ 7

Вот что я собрал:

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

Я новичок, поэтому возьмите это с солью, но, похоже, она работает.

Ответ 8

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?"

...

Ответ 9

Ты хочешь:

  • Встроенные команды Bash (например, переносные)
  • Проверка TTY
  • Ответ по умолчанию
  • Тайм-аут
  • Цветной вопрос

отрывок

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

Пояснения

  • [[ -t 0 ]] && read... => Вызов команды read если TTY
  • read -n 1 => Подождите один символ
  • $'\e[1;32m... \e[0m ' => Печать зеленым цветом
    (зеленый - это хорошо, потому что он читается как на белом, так и на черном фоне)
  • [[ $do_xxxx =~ ^(y|Y|)$ ]] => bash regex

Таймаут => Ответ по умолчанию - Нет

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

Ответ 10

Самый простой способ добиться этого с наименьшим числом строк:

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

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

if - это просто пример: вам решать, как обращаться с этой переменной.

Ответ 11

Используйте команду read:

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

а затем все остальные вещи, которые вам нужны

Ответ 12

Это решение считывает один символ и вызывает функцию ответа yes.

read -p "Are you sure? (y/n) " -n 1
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
    do_something      
fi

Ответ 13

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

Опция -e позволяет пользователю редактировать вход с помощью клавиш со стрелками.

Если вы хотите использовать предложение как ввод:

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

-i выводит наводящий ввод.

Ответ 14

Вы можете использовать значение по умолчанию REPLY для read, преобразовать его в нижний регистр и сравнить с набором переменных с выражением.
Сценарий также поддерживает 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

Ответ 15

Извините за размещение на таком старом посту. Несколько недель назад у меня возникла аналогичная проблема, в моем случае мне понадобилось решение, которое также работало в онлайн-установщике - script, например: curl -Ss https://raw.github.com/_____/installer.sh | bash

Использование read yesno < /dev/tty отлично подходит для меня:

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

Надеюсь, это поможет кому-то.

Ответ 16

Чтобы получить красивое входное окно с ncurses, используйте команду dialog следующим образом:

#!/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 do something risky"
    # do something risky
else 
    echo "Let stay boring"
fi

Диалоговый пакет устанавливается по умолчанию, по крайней мере, с SUSE Linux.

Ответ 17

Только одно нажатие клавиши

Здесь более длинный, но многоразовый и модульный подход:

  • Возвращает 0= да и 1= нет
  • Не требуется вводить требуемый пароль - всего один символ
  • Можно нажать enter, чтобы принять выбор по умолчанию
  • Может отключить выбор по умолчанию для принудительного выбора
  • Работает как для zsh и для bash.

По умолчанию "нет" при нажатии enter

Обратите внимание, что N является капиталом. Здесь вводится нажатие, принимающее значение по умолчанию:

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

Также обратите внимание, что [y/N]? автоматически добавляется. Значение по умолчанию "нет" принимается, поэтому ничего не отражается.

Повторно отправьте запрос до получения действительного ответа:

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

По умолчанию "да" при нажатии enter

Обратите внимание, что Y капитализируется:

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

Выше, я просто нажал enter, поэтому команда запустилась.

По умолчанию нет enter - требуется y или n

$ 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

Здесь было возвращено 1 или false. Обратите внимание, что с помощью этой функции нижнего уровня вам необходимо предоставить свой собственный [y/n]? незамедлительный.

Код

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

Ответ 18

Я заметил, что никто не отправил ответ, показывающий многострочное эхо-меню для такого простого пользовательского ввода, так что вот мой:

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

Этот метод был отправлен в надежде, что кто-то найдет его полезным и экономит время.

Ответ 19

Я предлагаю вам использовать диалог...

Linux Apprentice: Улучшение Bash Сценарии командной оболочки с использованием диалога

Команда диалога позволяет использовать окна в сценариях оболочки, чтобы сделать их использование более интерактивным.

он прост и удобен в использовании, также есть версия gnome, называемая gdialog, которая принимает точно такие же параметры, но показывает ее стиль GUI на X.

Ответ 20

Вариант с несколькими вариантами:

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
}

Пример:

$ 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
$

Он установит REPLY в y (внутри script).

Ответ 21

Вдохновленный ответами @Mark и @Myrddin, я создал эту функцию для универсального приглашения

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
}

используйте его следующим образом:

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

Ответ 22

более общим будет:

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
}

Ответ 23

Один простой способ сделать это - xargs -p или gnu parallel --interactive.

Мне нравится поведение xargs немного лучше для этого, потому что он выполняет каждую команду сразу после приглашения, как и другие интерактивные команды unix, вместо того, чтобы собирать yesses для запуска в конце. (Вы можете Ctrl-C после того, как вы пройдете те, которые вам нужны.)

например,

echo *.xml | xargs -p -n 1 -J {} mv {} backup/

Ответ 24

В качестве друга одной строки я использовал следующее:

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=;

Написанная длинная фигура работает следующим образом:

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=;

Ответ 25

Я использовал инструкцию case пару раз в таком сценарии, используя аргумент case - это хороший способ сделать это. Цикл while, который реализует блок case, который использует логическое условие, может быть реализован для того, чтобы еще больше контролировать программу и выполнять многие другие требования. После того, как все условия будут выполнены, можно использовать break, который передаст управление обратно в основную часть программы. Кроме того, для удовлетворения других условий, конечно, могут быть добавлены условные утверждения для сопровождения структур управления: оператор case и возможный цикл while.

Пример использования оператора case для выполнения вашего запроса

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

Ответ 26

Да/Нет/Отменить

функция

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

использование

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

Подтвердить с помощью чистого пользовательского ввода

функция

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

использование

if @confirm 'Confirm?' ; then
  echo "Yes"
else
  echo "No"
fi

Ответ 27

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!'

Ответ 28

В ответ другим:

Вам не нужно указывать случай в BASH4, просто используйте ",", чтобы сделать var в нижнем регистре. Также я сильно не люблю помещать код внутри блока чтения, получать результат и обрабатывать его вне блока чтения IMO. Также укажите "q" для выхода из IMO. И наконец, зачем "да" просто использовать -n1 и нажать y.

Пример: пользователь может нажать y/n, а также q, чтобы просто выйти.

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

Ответ 29

Можно обрабатывать "Да/Нет выбор" с учетом локали в оболочке POSIX; используя записи категории локали LC_MESSAGES, witch предоставляет готовые шаблоны RegEx для соответствия входным данным и строки для локализованного Да Нет.

#!/usr/bin/env sh

# Getting LC_MESSAGES values into variales
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

Ответ 30

С php, это мощно для сценариев оболочки:

#!/usr/bin/php

<?php 

$yo = readline("Do you wish to install this program? [Y/y] or [ctrl+c to quit]");

if ($yo == "Y" || $yo == "y") {   
    echo "Installing..";
    system("sudo apt install ...");
 }

Для этого необходим пакет php-cli.