如何在Linux shell脚本中提示输入Yes/No/Cancel ?

时间:2022-04-29 07:12:13

I want to pause input in a shell script, and prompt the user for choices. The standard 'Yes, No, or Cancel' type question. How do I accomplish this in a typical bash prompt?

我想暂停shell脚本中的输入,并提示用户进行选择。标准的“是、否或取消”类型问题。如何在典型的bash提示符中实现这一点?

26 个解决方案

#1


1249  

The simplest and most widely available method to get user input at a shell prompt is the read command. The best way to illustrate its use is a simple demonstration:

在shell提示符下获取用户输入的最简单、最广泛的方法是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

Another method, pointed out by Steven Huwig, is Bash's select command. Here is the same example using select:

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

With select you don't need to sanitize the input – it displays the available choices, and you type a number corresponding to your choice. It also loops automatically, so there's no need for a while true loop to retry if they give invalid input.

使用select,您不需要对输入进行消毒——它将显示可用的选项,然后您键入一个与您的选择相对应的数字。它也会自动循环,所以如果它们给出无效的输入,就不需要一个while循环来重试。

Also, please check out the excellent answer by F. Hauri.

另外,请查看F. Hauri的精彩答案。

#2


363  

At least five answers for one generic question.

Depending on

根据

  • compliant: could work on poor systems with generic environments
  • posix兼容:可以在具有通用shell环境的糟糕系统上工作
  • specific: using so called bashisms
  • bash专用:使用所谓的bashisms。

and if you want

如果你想

  • simple ``in line'' question / answer (generic solutions)
  • 简单的“in line”问题/答案(通用解决方案)
  • pretty formatted interfaces, like or more graphical using libgtk or libqt...
  • 漂亮的格式化界面,比如ncurses或者使用libgtk或libqt的图形化界面……
  • use powerful readline history capability
  • 使用强大的读线历史功能

1. POSIX generic solutions

You could use the read command, followed by if ... then ... else:

您可以使用read命令,然后是if…然后……其他:

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

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

#   (Thanks to Adam Katz's comment: This test is more portable and avoid one fork:)

#(感谢Adam Katz的评论:这个测试更便携,并且避免使用一个fork:)

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

POSIX, but single key feature

But if you don't want the user to have to hit Return, you could write:

但如果你不想让用户点击回车,你可以这样写:

(Edited: As @JonathanLeffler rightly suggest, saving stty's configuration could be better than simply force them to sane.)

(编辑:正如@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

Note: This was tested under , , , and !

注意:这是在sh, bash, ksh, dash和busybox下测试的!

Same, but waiting explicitly for y or n:

同样,但明确地等待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

Using dedicated tools

There are many tools which were built using libncurses, libgtk, libqt or other graphical libraries. For example, using whiptail:

有许多使用libncurses、libgtk、libqt或其他图形库构建的工具。例如,使用鞭尾:

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

Depending on your system, you may need to replace whiptail with another similiar tool:

根据您的系统,您可能需要用另一个类似工具替换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

where 20 is height of dialog box in number of lines and 60 is width of the dialog box. These tools all have near same syntax.

其中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 specific solutions

Basic in line method

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

I prefer to use case so I could even test for yes | ja | si | oui if needed...

我更喜欢用例,这样我甚至可以测试yes | ja |如果需要的话,|是的…

in line with single key feature

Under bash, we can specify the length of intended input for for the read command:

在bash中,我们可以为read命令指定预期输入的长度:

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

Under bash, read command accepts a timeout parameter, which could be useful.

在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

Some tricks for dedicated tools

More sophisticated dialog boxes, beyond simple yes - no purposes:

更复杂的对话框,除了简单的yes—no purpose:

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

Progress bar:

进度条:

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
) 

Little 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

More sample? Have a look at Using whiptail for choosing USB device and USB removable storage selector: USBKeyChooser

更多的样本吗?看看使用whiptail选择USB设备和USB移动存储选择器:USBKeyChooser

5. Using readline's history

Example:

例子:

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

This will create a file .myscript.history in your $HOME directory, than you could use readline's history commands, like Up, Down, Ctrl+r and others.

这将创建一个文件。myscript。在$HOME目录中的历史,比使用readline的历史命令要多,比如向上、向下、Ctrl+r等。

#3


332  

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

#4


115  

You can use the built-in read command ; Use the -p option to prompt the user with a question.

可以使用内置的read命令;使用-p选项提示用户提出问题。

Since BASH4, you can now use -i to suggest an answer, so the user only have to press return to enter it :

由于BASH4现在可以使用-i来建议答案,因此用户只需按return即可进入:

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

(But remember to use the "readline" option -e to allow line editing with arrow keys)

(但请记住使用“readline”选项-e允许使用箭头键进行行编辑)

#5


92  

Bash has select for this purpose.

Bash已经为此选择了。

select result in Yes No Cancel
do
    echo $result
done

#6


51  

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


27  

Here's something I put together:

这是我整理的东西:

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

I'm a beginner, so take this with a grain of salt, but it seems to work.

我是一个初学者,所以对此持怀疑态度,但这似乎行得通。

#8


22  

You want:

  • Bash builtin commands (i.e. portable)
  • Bash内置命令(即可移植)
  • Check TTY
  • 检查遥控
  • Default answer
  • 默认的答案
  • Timeout
  • 超时
  • Colored question
  • 颜色的问题

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

Explanations

  • [[ -t 0 ]] && read ... => Call command read if TTY
  • [- t0] && & read…=>调用命令读取if TTY
  • read -n 1 => Wait for one character
  • 读取- n1 =>等待一个字符
  • $'\e[1;32m ... \e[0m ' => Print in green
    (green is fine because readable on both white/black backgrounds)
  • “\ e[1;32美元……\e[0m ' =>绿色打印(绿色可以,因为白色/黑色背景都可以阅读)
  • [[ $do_xxxx =~ ^(y|Y|)$ ]] => bash regex
  • [[$ do_xxxx = ~ ^(y y | |)美元]]= > bash正则表达式

Timeout => Default answer is No

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

#9


20  

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

...

#10


19  

The easiest way to achieve this with the least number of lines is as follows:

用最少的行数来实现这个最简单的方法是:

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

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

The if is just an example: it is up to you how to handle this variable.

if只是一个例子:如何处理这个变量取决于您。

#11


15  

This solution reads a single character and calls a function on a yes response.

该解决方案读取单个字符并在yes响应上调用函数。

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

#12


15  

Use the read command:

使用读取命令:

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

and then all of the other stuff you need

然后你需要的其他东西。

#13


12  

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

The -e option enables the user to edit the input using arrow keys.

e选项允许用户使用箭头键编辑输入。

If you want to use a suggestion as input:

如果你想用一个建议作为输入:

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

-i option prints a suggestive input.

-i选项打印提示输入。

#14


7  

Sorry for posting on such an old post. Some weeks ago I was facing a similar problem, in my case I needed a solution which also worked within an online installer-script, eg: curl -Ss https://raw.github.com/_____/installer.sh | bash

很抱歉在这么旧的帖子上发帖。几个星期前,我遇到了一个类似的问题,在我的例子中,我需要一个同样适用于在线安装脚本的解决方案,例如:curl -Ss https://raw.github.com/_____/installer.sh | bash

Using read yesno < /dev/tty works fine for me:

使用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

Hope this helps someone.

希望这可以帮助别人。

#15


5  

To get a nice ncurses-like inputbox use the command dialog like this:

要获得一个类似ncurses的输入框,请使用如下所示的命令对话框:

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

The dialog package is installed by default at least with SUSE Linux.

至少在SUSE Linux中,对话框包是默认安装的。

#16


3  

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
}

Example:

例子:

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

It will set REPLY to y (inside the script).

它将对y(脚本内部)进行回复。

#17


3  

I suggest you use dialog...

我建议你使用对话……

Linux Apprentice: Improve Bash Shell Scripts Using Dialog

The dialog command enables the use of window boxes in shell scripts to make their use more interactive.

对话框命令允许在shell脚本中使用窗口框,使它们的使用更具交互性。

it's simple and easy to use, there's also a gnome version called gdialog that takes the exact same parameters, but shows it GUI style on X.

它简单易用,还有一个名为gdialog的gnome版本,它接受相同的参数,但是在X上显示GUI样式。

#18


3  

Inspired by the answers of @Mark and @Myrddin I created this function for a universal prompt

受到@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
}

use it like this:

使用它是这样的:

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

#19


3  

One simple way to do this is with xargs -p or gnu parallel --interactive.

一种简单的方法是使用xargs -p或gnu并行交互。

I like the behavior of xargs a little better for this because it executes each command immediately after the prompt like other interactive unix commands, rather than collecting the yesses to run at the end. (You can Ctrl-C after you get through the ones you wanted.)

我更喜欢xargs的行为,因为它像其他交互式unix命令一样,在提示符之后立即执行每个命令,而不是在最后收集要运行的yess。(您可以按Ctrl-C完成所需的操作。)

e.g.,

例如,

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

#20


3  

more generic would be:

更一般的是:

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
}

#21


2  

As a friend of a one line command I used the following:

作为一个单行命令的朋友,我使用了以下命令:

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

Written longform, it works like this:

写的很长,是这样的:

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

#22


2  

I've used the case statement a couple of times in such a scenario, using the case statment is a good way to go about it. A while loop, that ecapsulates the case block, that utilizes a boolean condition can be implemented in order to hold even more control of the program, and fulfill many other requirements. After the all the conditions have been met, a break can be used which will pass control back to the main part of the program. Also, to meet other conditions, of course conditional statements can be added to accompany the control structures: case statement and possible while loop.

在这样的场景中,我多次使用case语句,使用case statment是一种很好的方式。一个while循环,即ecapsulates这个case块,它利用一个布尔条件来实现对程序的更多控制,并满足许多其他的需求。在所有条件都满足之后,可以使用一个中断,它将把控制传回程序的主要部分。此外,为了满足其他条件,当然可以在控制结构中添加条件语句:case语句和可能的while循环。

Example of using a case statement to fulfill your request

示例使用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

#23


2  

I noticed that no one posted an answer showing multi-line echo menu for such simple user input so here is my go at it:

我注意到没有人贴出这样简单的用户输入的多行回波菜单的答案,所以我这样做:

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

This method was posted in the hopes that someone may find it useful and time-saving.

这个方法被发布是希望有人能发现它有用和节省时间。

#24


1  

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

#25


0  

Yes / No / Cancel

Function

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

Usage

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

Confirm with clean user input

Function

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

Usage

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

#26


0  

In response to others:

在回答别人:

You don't need to specify case in BASH4 just use the ',,' to make a var lowercase. Also I strongly dislike putting code inside of the read block, get the result and deal with it outside of the read block IMO. Also include a 'q' for quit IMO. Lastly why type 'yes' just use -n1 and have the press y.

您不需要在BASH4中指定大小写,只需使用“,”将var设置为小写。我也非常不喜欢把代码放在读块中,得到结果并在读块之外处理它。还包括一个“退出国际海事组织”的“q”。最后,为什么输入“yes”只需要使用-n1,然后按y键。

Example: user can press y/n and also q to just quit.

示例:用户可以按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

#1


1249  

The simplest and most widely available method to get user input at a shell prompt is the read command. The best way to illustrate its use is a simple demonstration:

在shell提示符下获取用户输入的最简单、最广泛的方法是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

Another method, pointed out by Steven Huwig, is Bash's select command. Here is the same example using select:

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

With select you don't need to sanitize the input – it displays the available choices, and you type a number corresponding to your choice. It also loops automatically, so there's no need for a while true loop to retry if they give invalid input.

使用select,您不需要对输入进行消毒——它将显示可用的选项,然后您键入一个与您的选择相对应的数字。它也会自动循环,所以如果它们给出无效的输入,就不需要一个while循环来重试。

Also, please check out the excellent answer by F. Hauri.

另外,请查看F. Hauri的精彩答案。

#2


363  

At least five answers for one generic question.

Depending on

根据

  • compliant: could work on poor systems with generic environments
  • posix兼容:可以在具有通用shell环境的糟糕系统上工作
  • specific: using so called bashisms
  • bash专用:使用所谓的bashisms。

and if you want

如果你想

  • simple ``in line'' question / answer (generic solutions)
  • 简单的“in line”问题/答案(通用解决方案)
  • pretty formatted interfaces, like or more graphical using libgtk or libqt...
  • 漂亮的格式化界面,比如ncurses或者使用libgtk或libqt的图形化界面……
  • use powerful readline history capability
  • 使用强大的读线历史功能

1. POSIX generic solutions

You could use the read command, followed by if ... then ... else:

您可以使用read命令,然后是if…然后……其他:

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

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

#   (Thanks to Adam Katz's comment: This test is more portable and avoid one fork:)

#(感谢Adam Katz的评论:这个测试更便携,并且避免使用一个fork:)

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

POSIX, but single key feature

But if you don't want the user to have to hit Return, you could write:

但如果你不想让用户点击回车,你可以这样写:

(Edited: As @JonathanLeffler rightly suggest, saving stty's configuration could be better than simply force them to sane.)

(编辑:正如@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

Note: This was tested under , , , and !

注意:这是在sh, bash, ksh, dash和busybox下测试的!

Same, but waiting explicitly for y or n:

同样,但明确地等待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

Using dedicated tools

There are many tools which were built using libncurses, libgtk, libqt or other graphical libraries. For example, using whiptail:

有许多使用libncurses、libgtk、libqt或其他图形库构建的工具。例如,使用鞭尾:

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

Depending on your system, you may need to replace whiptail with another similiar tool:

根据您的系统,您可能需要用另一个类似工具替换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

where 20 is height of dialog box in number of lines and 60 is width of the dialog box. These tools all have near same syntax.

其中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 specific solutions

Basic in line method

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

I prefer to use case so I could even test for yes | ja | si | oui if needed...

我更喜欢用例,这样我甚至可以测试yes | ja |如果需要的话,|是的…

in line with single key feature

Under bash, we can specify the length of intended input for for the read command:

在bash中,我们可以为read命令指定预期输入的长度:

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

Under bash, read command accepts a timeout parameter, which could be useful.

在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

Some tricks for dedicated tools

More sophisticated dialog boxes, beyond simple yes - no purposes:

更复杂的对话框,除了简单的yes—no purpose:

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

Progress bar:

进度条:

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
) 

Little 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

More sample? Have a look at Using whiptail for choosing USB device and USB removable storage selector: USBKeyChooser

更多的样本吗?看看使用whiptail选择USB设备和USB移动存储选择器:USBKeyChooser

5. Using readline's history

Example:

例子:

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

This will create a file .myscript.history in your $HOME directory, than you could use readline's history commands, like Up, Down, Ctrl+r and others.

这将创建一个文件。myscript。在$HOME目录中的历史,比使用readline的历史命令要多,比如向上、向下、Ctrl+r等。

#3


332  

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

#4


115  

You can use the built-in read command ; Use the -p option to prompt the user with a question.

可以使用内置的read命令;使用-p选项提示用户提出问题。

Since BASH4, you can now use -i to suggest an answer, so the user only have to press return to enter it :

由于BASH4现在可以使用-i来建议答案,因此用户只需按return即可进入:

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

(But remember to use the "readline" option -e to allow line editing with arrow keys)

(但请记住使用“readline”选项-e允许使用箭头键进行行编辑)

#5


92  

Bash has select for this purpose.

Bash已经为此选择了。

select result in Yes No Cancel
do
    echo $result
done

#6


51  

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


27  

Here's something I put together:

这是我整理的东西:

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

I'm a beginner, so take this with a grain of salt, but it seems to work.

我是一个初学者,所以对此持怀疑态度,但这似乎行得通。

#8


22  

You want:

  • Bash builtin commands (i.e. portable)
  • Bash内置命令(即可移植)
  • Check TTY
  • 检查遥控
  • Default answer
  • 默认的答案
  • Timeout
  • 超时
  • Colored question
  • 颜色的问题

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

Explanations

  • [[ -t 0 ]] && read ... => Call command read if TTY
  • [- t0] && & read…=>调用命令读取if TTY
  • read -n 1 => Wait for one character
  • 读取- n1 =>等待一个字符
  • $'\e[1;32m ... \e[0m ' => Print in green
    (green is fine because readable on both white/black backgrounds)
  • “\ e[1;32美元……\e[0m ' =>绿色打印(绿色可以,因为白色/黑色背景都可以阅读)
  • [[ $do_xxxx =~ ^(y|Y|)$ ]] => bash regex
  • [[$ do_xxxx = ~ ^(y y | |)美元]]= > bash正则表达式

Timeout => Default answer is No

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

#9


20  

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

...

#10


19  

The easiest way to achieve this with the least number of lines is as follows:

用最少的行数来实现这个最简单的方法是:

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

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

The if is just an example: it is up to you how to handle this variable.

if只是一个例子:如何处理这个变量取决于您。

#11


15  

This solution reads a single character and calls a function on a yes response.

该解决方案读取单个字符并在yes响应上调用函数。

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

#12


15  

Use the read command:

使用读取命令:

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

and then all of the other stuff you need

然后你需要的其他东西。

#13


12  

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

The -e option enables the user to edit the input using arrow keys.

e选项允许用户使用箭头键编辑输入。

If you want to use a suggestion as input:

如果你想用一个建议作为输入:

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

-i option prints a suggestive input.

-i选项打印提示输入。

#14


7  

Sorry for posting on such an old post. Some weeks ago I was facing a similar problem, in my case I needed a solution which also worked within an online installer-script, eg: curl -Ss https://raw.github.com/_____/installer.sh | bash

很抱歉在这么旧的帖子上发帖。几个星期前,我遇到了一个类似的问题,在我的例子中,我需要一个同样适用于在线安装脚本的解决方案,例如:curl -Ss https://raw.github.com/_____/installer.sh | bash

Using read yesno < /dev/tty works fine for me:

使用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

Hope this helps someone.

希望这可以帮助别人。

#15


5  

To get a nice ncurses-like inputbox use the command dialog like this:

要获得一个类似ncurses的输入框,请使用如下所示的命令对话框:

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

The dialog package is installed by default at least with SUSE Linux.

至少在SUSE Linux中,对话框包是默认安装的。

#16


3  

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
}

Example:

例子:

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

It will set REPLY to y (inside the script).

它将对y(脚本内部)进行回复。

#17


3  

I suggest you use dialog...

我建议你使用对话……

Linux Apprentice: Improve Bash Shell Scripts Using Dialog

The dialog command enables the use of window boxes in shell scripts to make their use more interactive.

对话框命令允许在shell脚本中使用窗口框,使它们的使用更具交互性。

it's simple and easy to use, there's also a gnome version called gdialog that takes the exact same parameters, but shows it GUI style on X.

它简单易用,还有一个名为gdialog的gnome版本,它接受相同的参数,但是在X上显示GUI样式。

#18


3  

Inspired by the answers of @Mark and @Myrddin I created this function for a universal prompt

受到@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
}

use it like this:

使用它是这样的:

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

#19


3  

One simple way to do this is with xargs -p or gnu parallel --interactive.

一种简单的方法是使用xargs -p或gnu并行交互。

I like the behavior of xargs a little better for this because it executes each command immediately after the prompt like other interactive unix commands, rather than collecting the yesses to run at the end. (You can Ctrl-C after you get through the ones you wanted.)

我更喜欢xargs的行为,因为它像其他交互式unix命令一样,在提示符之后立即执行每个命令,而不是在最后收集要运行的yess。(您可以按Ctrl-C完成所需的操作。)

e.g.,

例如,

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

#20


3  

more generic would be:

更一般的是:

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
}

#21


2  

As a friend of a one line command I used the following:

作为一个单行命令的朋友,我使用了以下命令:

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

Written longform, it works like this:

写的很长,是这样的:

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

#22


2  

I've used the case statement a couple of times in such a scenario, using the case statment is a good way to go about it. A while loop, that ecapsulates the case block, that utilizes a boolean condition can be implemented in order to hold even more control of the program, and fulfill many other requirements. After the all the conditions have been met, a break can be used which will pass control back to the main part of the program. Also, to meet other conditions, of course conditional statements can be added to accompany the control structures: case statement and possible while loop.

在这样的场景中,我多次使用case语句,使用case statment是一种很好的方式。一个while循环,即ecapsulates这个case块,它利用一个布尔条件来实现对程序的更多控制,并满足许多其他的需求。在所有条件都满足之后,可以使用一个中断,它将把控制传回程序的主要部分。此外,为了满足其他条件,当然可以在控制结构中添加条件语句:case语句和可能的while循环。

Example of using a case statement to fulfill your request

示例使用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

#23


2  

I noticed that no one posted an answer showing multi-line echo menu for such simple user input so here is my go at it:

我注意到没有人贴出这样简单的用户输入的多行回波菜单的答案,所以我这样做:

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

This method was posted in the hopes that someone may find it useful and time-saving.

这个方法被发布是希望有人能发现它有用和节省时间。

#24


1  

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

#25


0  

Yes / No / Cancel

Function

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

Usage

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

Confirm with clean user input

Function

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

Usage

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

#26


0  

In response to others:

在回答别人:

You don't need to specify case in BASH4 just use the ',,' to make a var lowercase. Also I strongly dislike putting code inside of the read block, get the result and deal with it outside of the read block IMO. Also include a 'q' for quit IMO. Lastly why type 'yes' just use -n1 and have the press y.

您不需要在BASH4中指定大小写,只需使用“,”将var设置为小写。我也非常不喜欢把代码放在读块中,得到结果并在读块之外处理它。还包括一个“退出国际海事组织”的“q”。最后,为什么输入“yes”只需要使用-n1,然后按y键。

Example: user can press y/n and also q to just quit.

示例:用户可以按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